255 lines
5.1 KiB
C
255 lines
5.1 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/un.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <unistd.h>
|
||
|
#include <errno.h>
|
||
|
#include <sys/poll.h>
|
||
|
#include <arpa/inet.h>
|
||
|
#include <ctype.h>
|
||
|
#include <signal.h>
|
||
|
#include <netdb.h>
|
||
|
#include <fcntl.h>
|
||
|
#include "libtel.h"
|
||
|
|
||
|
void handle_call(int sock) {
|
||
|
fcntl(0, F_SETFL, O_NONBLOCK);
|
||
|
fcntl(sock, F_SETFL, O_NONBLOCK);
|
||
|
struct pollfd fds[2];
|
||
|
fds[0].fd = 0;
|
||
|
fds[0].events = POLLIN | POLLERR | POLLHUP;
|
||
|
fds[1].fd = sock;
|
||
|
fds[1].events = POLLIN | POLLERR | POLLHUP;
|
||
|
|
||
|
while(1) {
|
||
|
char buf[1024];
|
||
|
int res = poll(fds, 2, -1);
|
||
|
if(res < 0) {
|
||
|
perror("error polling");
|
||
|
goto end_err;
|
||
|
}
|
||
|
|
||
|
if(fds[0].revents & POLLIN) {
|
||
|
while((res = read(0, buf, 1024)) > 0) {
|
||
|
res = tel_write_all(sock, buf, res);
|
||
|
if(res < 0) {
|
||
|
perror("failed to send message");
|
||
|
goto end_err;
|
||
|
}
|
||
|
}
|
||
|
if(res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||
|
perror("failed to read input");
|
||
|
goto end_err;
|
||
|
}
|
||
|
}
|
||
|
if(fds[1].revents & POLLIN) {
|
||
|
int readcount = 0;
|
||
|
while((res = read(sock, buf, 1024)) > 0) {
|
||
|
printf("%.*s", res, buf);
|
||
|
readcount++;
|
||
|
}
|
||
|
if(res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
|
||
|
perror("failed to receive message");
|
||
|
goto end_err;
|
||
|
}
|
||
|
if(readcount == 0) {
|
||
|
printf("peer terminated call\n");
|
||
|
goto end;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
end:
|
||
|
close(sock);
|
||
|
exit(0);
|
||
|
end_err:
|
||
|
close(sock);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
int connect_local_sock(char *path) {
|
||
|
int res;
|
||
|
int sock;
|
||
|
struct sockaddr_un addr;
|
||
|
|
||
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||
|
addr.sun_family = AF_UNIX;
|
||
|
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
|
||
|
|
||
|
sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||
|
if(sock < 0) {
|
||
|
perror("error opening local socket");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
res = connect(sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un));
|
||
|
if(res < 0) {
|
||
|
perror("error connecting to local socket");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
return sock;
|
||
|
}
|
||
|
|
||
|
void pickup(struct sockaddr *addr) {
|
||
|
int res;
|
||
|
int sock = connect_local_sock("/tmp/tel.sock");
|
||
|
|
||
|
res = tel_write_u32(sock, MSG_PICKUP);
|
||
|
if(res < 0) goto write_err;
|
||
|
|
||
|
res = tel_write_sockaddr(sock, addr);
|
||
|
if(res < 0) goto write_err;
|
||
|
|
||
|
uint32_t rsp;
|
||
|
res = tel_read_u32(sock, &rsp);
|
||
|
if(res < 0) goto read_err;
|
||
|
|
||
|
switch(rsp) {
|
||
|
case MSG_NONE:
|
||
|
printf("no incoming calls from specified address\n");
|
||
|
close(sock);
|
||
|
return;
|
||
|
case MSG_ERROR: {
|
||
|
uint32_t len;
|
||
|
res = tel_read_u32(sock, &len);
|
||
|
if(res < 0) goto read_err;
|
||
|
char buf[len + 1];
|
||
|
res = tel_read_all(sock, buf, len);
|
||
|
if(res < 0) goto read_err;
|
||
|
buf[len] = '\0';
|
||
|
fprintf(stderr, "error in daemon: %s\n", buf);
|
||
|
close(sock);
|
||
|
return;
|
||
|
}
|
||
|
case MSG_SOCKET: {
|
||
|
int fd;
|
||
|
int res = tel_read_fd(sock, &fd);
|
||
|
if(res < 0) {
|
||
|
perror("failed to receive fd");
|
||
|
return;
|
||
|
} else if(res == 0) {
|
||
|
fprintf(stderr, "failed to receive fd: no data\n");
|
||
|
return;
|
||
|
}
|
||
|
close(sock);
|
||
|
printf("picked up call\n");
|
||
|
handle_call(fd);
|
||
|
return;
|
||
|
}
|
||
|
default:
|
||
|
close(sock);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
read_err:
|
||
|
perror("error reading socket");
|
||
|
goto end;
|
||
|
write_err:
|
||
|
perror("error writing socket");
|
||
|
goto end;
|
||
|
end:
|
||
|
close(sock);
|
||
|
}
|
||
|
|
||
|
void call(struct sockaddr *addr) {
|
||
|
int res;
|
||
|
int sock = connect_local_sock("/tmp/tel.sock");
|
||
|
|
||
|
res = tel_write_u32(sock, MSG_CALL);
|
||
|
if(res < 0) goto write_err;
|
||
|
|
||
|
res = tel_write_sockaddr(sock, addr);
|
||
|
if(res < 0) goto write_err;
|
||
|
|
||
|
uint32_t rsp;
|
||
|
res = tel_read_u32(sock, &rsp);
|
||
|
if(res < 0) goto read_err;
|
||
|
|
||
|
switch(rsp) {
|
||
|
case MSG_NONE:
|
||
|
printf("none\n");
|
||
|
close(sock);
|
||
|
return;
|
||
|
case MSG_ERROR: {
|
||
|
uint32_t len;
|
||
|
res = tel_read_u32(sock, &len);
|
||
|
if(res < 0) goto read_err;
|
||
|
char buf[len + 1];
|
||
|
res = tel_read_all(sock, buf, len);
|
||
|
if(res < 0) goto read_err;
|
||
|
buf[len] = '\0';
|
||
|
fprintf(stderr, "error in daemon: %s\n", buf);
|
||
|
close(sock);
|
||
|
return;
|
||
|
}
|
||
|
case MSG_SOCKET: {
|
||
|
int fd;
|
||
|
int res = tel_read_fd(sock, &fd);
|
||
|
if(res < 0) {
|
||
|
perror("failed to receive fd");
|
||
|
return;
|
||
|
} else if(res == 0) {
|
||
|
fprintf(stderr, "failed to receive fd: no data\n");
|
||
|
return;
|
||
|
}
|
||
|
close(sock);
|
||
|
printf("calling...\n");
|
||
|
handle_call(fd);
|
||
|
return;
|
||
|
}
|
||
|
default:
|
||
|
close(sock);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
read_err:
|
||
|
perror("error reading socket");
|
||
|
goto end;
|
||
|
write_err:
|
||
|
perror("error writing socket");
|
||
|
goto end;
|
||
|
end:
|
||
|
close(sock);
|
||
|
}
|
||
|
|
||
|
void usage_exit(char *name) {
|
||
|
fprintf(stderr, "usage: %s call <addr> [ip] OR %s pkup <addr> [ip]", name, name);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
int main(int argc, char **argv) {
|
||
|
if(argc < 1) exit(1);
|
||
|
if(argc < 2) usage_exit(argv[0]);
|
||
|
|
||
|
if(strcmp(argv[1], "call") == 0) {
|
||
|
if(argc < 3 || argc > 4) usage_exit(argv[0]);
|
||
|
|
||
|
struct sockaddr *addr;
|
||
|
socklen_t addr_len;
|
||
|
int res = tel_get_addr(argv[2], (argc < 4) ? NULL : argv[3], &addr, &addr_len);
|
||
|
if(res != 0) {
|
||
|
fprintf(stderr, "error: %s\n", gai_strerror(res));
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
call(addr);
|
||
|
|
||
|
} else if(strcmp(argv[1], "pkup") == 0) {
|
||
|
if(argc < 3 || argc > 4) usage_exit(argv[0]);
|
||
|
|
||
|
struct sockaddr *addr;
|
||
|
socklen_t addr_len;
|
||
|
int res = tel_get_addr(argv[2], (argc < 4) ? NULL : argv[3], &addr, &addr_len);
|
||
|
if(res != 0) {
|
||
|
fprintf(stderr, "error: %s\n", gai_strerror(res));
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
pickup(addr);
|
||
|
} else {
|
||
|
usage_exit(argv[0]);
|
||
|
}
|
||
|
}
|