#include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 [ip] OR %s pkup [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]); } }