initial commit

This commit is contained in:
trimill 2023-11-02 19:43:50 -04:00
commit 19ae6e9dcb
No known key found for this signature in database
GPG Key ID: 5FCAB0BC7C851657
6 changed files with 1048 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
tel
teld
tel.o
teld.o
libtel.o

25
Makefile Normal file
View File

@ -0,0 +1,25 @@
CFLAGS += -Wall -Wextra -pedantic
# CFLAGS += -O0 -g
CFLAGS += -O3
.phony: all clean
all: tel teld
clean:
rm -f tel teld tel.o teld.o libtel.o
libtel.o: libtel.c
$(CC) $(CFLAGS) -c libtel.c
tel.o: tel.c libtel.h
$(CC) $(CFLAGS) -c tel.c -ledit -lncurses
tel: tel.o libtel.o
$(CC) $(CFLAGS) tel.o libtel.o -o tel
teld.o: teld.c libtel.h
$(CC) $(CFLAGS) -c teld.c -ledit -lncurses
teld: teld.o libtel.o
$(CC) $(CFLAGS) teld.o libtel.o -o teld

329
libtel.c Normal file
View File

@ -0,0 +1,329 @@
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include "libtel.h"
int tel_sa_any_eq(union tel_sa_any *a, union tel_sa_any *b) {
struct sockaddr *as = (struct sockaddr *)a;
struct sockaddr *bs = (struct sockaddr *)b;
if(as->sa_family != bs->sa_family) {
return 0;
}
if(as->sa_family == AF_INET) {
struct sockaddr_in *a4 = &a->v4;
struct sockaddr_in *b4 = &b->v4;
if(a4->sin_port != b4->sin_port) {
return 0;
}
if(a4->sin_addr.s_addr != b4->sin_addr.s_addr) {
return 0;
}
return 1;
} else if(as->sa_family == AF_INET6) {
struct sockaddr_in6 *a6 = &a->v6;
struct sockaddr_in6 *b6 = &b->v6;
if(a6->sin6_port != b6->sin6_port) {
return 0;
}
if(memcmp(a6->sin6_addr.s6_addr, b6->sin6_addr.s6_addr, 16 * sizeof(uint8_t)) != 0) {
return 0;
}
return 1;
} else {
return 0;
}
}
int tel_get_addr(char *ip, char *port, struct sockaddr **addr, socklen_t *addr_len) {
if(port == NULL) port = "4111";
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
struct addrinfo *info;
int res = getaddrinfo(ip, port, &hints, &info);
if(res != 0) return res;
*addr = info->ai_addr;
*addr_len = info->ai_addrlen;
freeaddrinfo(info);
return 0;
}
int tel_straddr(const struct sockaddr *restrict addr, char *restrict buf) {
uint16_t port = 0;
if(addr->sa_family == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
port = addr4->sin_port;
const char *res = inet_ntop(AF_INET, &addr4->sin_addr, buf, INET6_ADDRSTRLEN);
if(res == NULL) {
return -1;
}
} else if(addr->sa_family == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
port = addr6->sin6_port;
const char *res = inet_ntop(AF_INET6, &addr6->sin6_addr, buf, INET6_ADDRSTRLEN);
if(res == NULL) {
return -1;
}
} else {
errno = 0;
return -1;
}
size_t len = strlen(buf);
sprintf(buf + len, " %u", ntohs(port));
return 0;
}
ssize_t tel_write_all(int fd, const void *buf, size_t count) {
const char *cbuf = (char *)buf;
ssize_t wc;
ssize_t total = 0;
while(count > 0) {
wc = write(fd, cbuf, count);
if(wc <= 0) {
return wc;
}
cbuf += wc;
count -= wc;
total += wc;
}
return total;
}
ssize_t tel_read_all(int fd, void *buf, size_t count) {
char *cbuf = (char *)buf;
ssize_t rc;
ssize_t total = 0;
while(count > 0) {
rc = read(fd, cbuf, count);
if(rc <= 0) {
return rc;
}
cbuf += rc;
count -= rc;
total += rc;
}
return total;
}
ssize_t tel_write_u32(int fd, uint32_t n) {
uint32_t nn = htonl(n);
char bytes[4];
memcpy(bytes, &nn, 4);
return tel_write_all(fd, bytes, 4);
}
ssize_t tel_read_u32(int fd, uint32_t *n) {
char bytes[4];
ssize_t res = tel_read_all(fd, bytes, 4);
if(res < 4) return res;
uint32_t nn;
memcpy(&nn, bytes, 4);
*n = ntohl(nn);
return res;
}
ssize_t tel_write_u16(int fd, uint16_t n) {
uint16_t nn = htons(n);
char bytes[2];
memcpy(bytes, &nn, 2);
return tel_write_all(fd, bytes, 2);
}
ssize_t tel_read_u16(int fd, uint16_t *n) {
char bytes[2];
ssize_t res = tel_read_all(fd, bytes, 2);
if(res < 2) return res;
uint16_t nn;
memcpy(&nn, bytes, 2);
*n = ntohs(nn);
return res;
}
ssize_t tel_write_fd(int stream, int fd) {
struct msghdr msg;
struct iovec iov[1];
struct cmsghdr *cmsg = NULL;
char ctrl_buf[CMSG_SPACE(sizeof(int))];
char data[1];
memset(&msg, 0, sizeof(struct msghdr));
memset(ctrl_buf, 0, CMSG_SPACE(sizeof(int)));
data[0] = '#';
iov[0].iov_base = data;
iov[0].iov_len = sizeof(data);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_controllen = CMSG_SPACE(sizeof(int));
msg.msg_control = ctrl_buf;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*((int *) CMSG_DATA(cmsg)) = fd;
return sendmsg(stream, &msg, 0);
}
ssize_t tel_read_fd(int sock, int *fd) {
int len;
char buf[1];
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
char cms[CMSG_SPACE(sizeof(int))];
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = (caddr_t) cms;
msg.msg_controllen = sizeof cms;
len = recvmsg(sock, &msg, 0);
if(len <= 0) {
return len;
}
cmsg = CMSG_FIRSTHDR(&msg);
memmove(fd, CMSG_DATA(cmsg), sizeof(int));
return len;
}
ssize_t tel_write_sockaddr(int fd, struct sockaddr *addr) {
ssize_t res;
ssize_t total = 0;
if(addr->sa_family == AF_INET) {
struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
res = tel_write_u32(fd, 4);
if(res < 0) return res;
total += res;
res = tel_write_u32(fd, addr4->sin_addr.s_addr);
if(res < 0) return res;
total += res;
res = tel_write_u16(fd, addr4->sin_port);
if(res < 0) return res;
total += res;
return total;
} else if(addr->sa_family == AF_INET6) {
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
res = tel_write_u32(fd, 6);
if(res < 0) return res;
total += res;
res = tel_write_all(fd, addr6->sin6_addr.s6_addr, 16);
if(res < 0) return res;
total += res;
res = tel_write_u16(fd, addr6->sin6_port);
if(res < 0) return res;
total += res;
} else {
errno = ESOCKTNOSUPPORT;
return -1;
}
return total;
}
ssize_t tel_read_sockaddr(int fd, union tel_sa_any *addr, socklen_t *addr_len) {
ssize_t res;
ssize_t total = 0;
uint32_t ty = 0;
res = tel_read_u32(fd, &ty);
if(res < 0) return res;
total += res;
if(ty == 4) {
if(*addr_len < sizeof(struct sockaddr_in)) {
printf("addr_len %u too small for ipv4\n", *addr_len);
errno = 0;
return -1;
}
struct sockaddr_in *addr4 = &addr->v4;
*addr_len = sizeof(struct sockaddr_in);
addr4->sin_family = AF_INET;
res = tel_read_u32(fd, &addr4->sin_addr.s_addr);
if(res < 0) return res;
total += res;
res = tel_read_u16(fd, &addr4->sin_port);
if(res < 0) return res;
total += res;
} else if(ty == 6) {
if(*addr_len < sizeof(struct sockaddr_in6)) {
printf("addr_len %u to small for ipv6\n", *addr_len);
errno = 0;
return -1;
}
struct sockaddr_in6 *addr6 = &addr->v6;
*addr_len = sizeof(struct sockaddr_in6);
addr6->sin6_family = AF_INET6;
res = tel_read_all(fd, addr6->sin6_addr.s6_addr, 16);
if(res < 0) return res;
total += res;
res = tel_read_u16(fd, &addr6->sin6_port);
if(res < 0) return res;
total += res;
} else {
printf("ty was %u\n", ty);
errno = 0;
return -1;
}
return total;
}
ssize_t tel_write_err(int fd, const char *msg) {
size_t msglen = strlen(msg);
ssize_t res1 = tel_write_u32(fd, MSG_ERROR);
if(res1 < 0) return res1;
ssize_t res2 = tel_write_u32(fd, msglen);
if(res2 < 0) return res2;
ssize_t res3 = tel_write_all(fd, msg, msglen);
if(res3 < 0) return res3;
return res1 + res2 + res3;
}

41
libtel.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include <sys/types.h>
#define TEL_STRADDRLEN (INET6_ADDRSTRLEN + 1 + 5)
union tel_sa_any {
struct sockaddr_in v4;
struct sockaddr_in6 v6;
};
int tel_sa_any_eq(union tel_sa_any *a, union tel_sa_any *b);
enum tel_message {
MSG_PICKUP,
MSG_CALL,
MSG_LIST,
MSG_SOCKET,
MSG_OK,
MSG_ERROR,
MSG_NONE,
};
int tel_get_addr(char *ip, char *port, struct sockaddr **addr, socklen_t *addr_len);
int tel_straddr(const struct sockaddr *restrict addr, char *restrict buf);
ssize_t tel_write_all(int fd, const void *buf, size_t count);
ssize_t tel_read_all(int fd, void *buf, size_t count);
ssize_t tel_write_u32(int fd, uint32_t n);
ssize_t tel_read_u32(int fd, uint32_t *n);
ssize_t tel_write_u16(int fd, uint16_t n);
ssize_t tel_read_u16(int fd, uint16_t *n);
ssize_t tel_write_fd(int stream, int fd);
ssize_t tel_read_fd(int sock, int *fd);
ssize_t tel_write_sockaddr(int fd, struct sockaddr *addr);
ssize_t tel_read_sockaddr(int fd, union tel_sa_any *addr, socklen_t *addr_len);
ssize_t tel_write_err(int fd, const char *msg);

254
tel.c Normal file
View File

@ -0,0 +1,254 @@
#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]);
}
}

394
teld.c Normal file
View File

@ -0,0 +1,394 @@
#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"
#define CALL_BUF_SIZE 16
// config
struct config {
struct sockaddr *addr;
socklen_t addr_len;
int connect_timeout;
};
struct in_call {
int fd;
union tel_sa_any addr;
};
//
// Call buffer operations
//
struct call_buf {
struct in_call buf[CALL_BUF_SIZE];
size_t len;
};
void call_buf_init(struct call_buf *buf) {
buf->len = 0;
}
void call_buf_add(struct call_buf *buf, struct in_call call) {
// if buffer is full, remove and close tail element
if(buf->len == CALL_BUF_SIZE) {
struct in_call last = buf->buf[0];
close(last.fd);
memmove(buf->buf, buf->buf + 1, CALL_BUF_SIZE - 1);
}
buf->buf[buf->len++] = call;
}
struct in_call call_buf_remove(struct call_buf *buf, int n) {
struct in_call call = buf->buf[n];
buf->len--;
buf->buf[n] = buf->buf[buf->len];
return call;
}
int call_buf_search(struct call_buf *buf, union tel_sa_any *callinfo) {
for(size_t i = 0; i < buf->len; i++) {
if(tel_sa_any_eq(&buf->buf[i].addr, callinfo)) {
return i;
}
}
return -1;
}
//
// Socket initialization
//
int init_client_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 client socket");
exit(1);
}
unlink(path);
res = bind(sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un));
if(res < 0) {
perror("error binding to client socket");
exit(1);
}
res = listen(sock, 16);
if(res < 0) {
perror("error listening on client socket");
exit(1);
}
printf("client socket listening at %s\n", path);
return sock;
}
int init_net_sock(struct sockaddr *addr, socklen_t addr_len) {
int res;
int sock;
sock = socket(addr->sa_family, SOCK_STREAM, 0);
if(sock < 0) {
perror("error opening network socket");
exit(1);
}
int enable = 1;
res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
if(res < 0) {
perror("failed to set socket option SO_REUSEADDR");
exit(1);
}
res = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int));
if(res < 0) {
perror("failed to set socket option SO_REUSEPORT");
exit(1);
}
res = bind(sock, addr, addr_len);
if(res < 0) {
perror("error binding to network socket");
exit(1);
}
res = listen(sock, 16);
if(res < 0) {
perror("error listening on network socket");
exit(1);
}
char buf[TEL_STRADDRLEN];
res = tel_straddr(addr, buf);
if(res >= 0) {
printf("network socket listening at %s\n", buf);
}
return sock;
}
void client_pickup(struct call_buf *call_buf, int stream) {
int res;
union tel_sa_any remote_addr;
socklen_t remote_addr_len = sizeof(union tel_sa_any);
res = tel_read_sockaddr(stream, &remote_addr, &remote_addr_len);
int idx = call_buf_search(call_buf, &remote_addr);
if(idx < 0) {
tel_write_u32(stream, MSG_NONE);
return;
}
struct in_call call = call_buf_remove(call_buf, idx);
int fd = call.fd;
tel_write_u32(stream, MSG_SOCKET);
res = tel_write_fd(stream, fd);
if(res < 0) {
tel_write_err(stream, strerror(errno));
};
close(fd);
return;
}
void client_call(int stream, struct config cfg) {
int res;
union tel_sa_any remote_addr;
socklen_t remote_addr_len = sizeof(union tel_sa_any);
res = tel_read_sockaddr(stream, &remote_addr, &remote_addr_len);
if(res < 0) goto err_noclose;
char buf[TEL_STRADDRLEN];
res = tel_straddr((struct sockaddr *)&remote_addr, buf);
if(res >= 0) {
printf("client calling %s...\n", buf);
}
int sock = socket(remote_addr.v4.sin_family, SOCK_STREAM, 0);
if(res < 0) goto err_noclose;
int enable = 1;
res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
if(res < 0) goto err;
res = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int));
if(res < 0) goto err;
res = bind(sock, cfg.addr, cfg.addr_len);
if(res < 0) goto err;
int sock_flags = fcntl(sock, F_GETFL);
fcntl(sock, F_SETFL, sock_flags | O_NONBLOCK);
struct pollfd pollfds[1];
pollfds[0].fd = sock;
pollfds[0].events = POLLOUT;
res = connect(sock, (struct sockaddr *)&remote_addr, remote_addr_len);
if(res < 0 && errno != EINPROGRESS) goto err;
res = poll(pollfds, 1, cfg.connect_timeout);
if(res < 0) goto err;
if(res == 0) {
printf("error making call: timed out\n");
tel_write_err(stream, "timed out");
close(sock);
return;
}
if(pollfds[0].revents & POLLERR) {
printf("error making call: timed out\n");
tel_write_err(stream, "unknown error connecting");
close(sock);
return;
}
fcntl(sock, F_SETFL, sock_flags);
res = tel_write_u32(stream, MSG_SOCKET);
if(res < 0) goto err;
res = tel_write_fd(stream, sock);
if(res < 0) goto err;
printf("call made successfully\n");
close(sock);
return;
err_noclose:
printf("error making call: %s\n", strerror(errno));
tel_write_err(stream, strerror(errno));
return;
err:
printf("error making call: %s\n", strerror(errno));
tel_write_err(stream, strerror(errno));
close(sock);
return;
}
void accept_client(struct call_buf *call_buf, int fd, struct config cfg) {
int stream = accept(fd, NULL, 0);
if(stream < 0) {
perror("error accepting client connection");
exit(1);
}
printf("accepted client connection\n");
enum tel_message msg;
int rc = tel_read_u32(stream, &msg);
if(rc < 0) {
tel_write_err(stream, strerror(errno));
return;
}
switch(msg) {
case MSG_PICKUP:
client_pickup(call_buf, stream);
break;
case MSG_CALL:
client_call(stream, cfg);
break;
default:
tel_write_err(stream, "invalid command");
break;
}
close(stream);
}
//
// Processing network connections
//
void accept_net(struct call_buf *call_buf, int fd) {
union tel_sa_any cli_addr;
socklen_t addr_len = sizeof(union tel_sa_any);
int stream = accept(fd, (struct sockaddr *)&cli_addr, &addr_len);
if(stream < 0) {
perror("error accepting network connection");
exit(1);
}
char buf[TEL_STRADDRLEN];
int res = tel_straddr((struct sockaddr *)&cli_addr, buf);
if(res >= 0) {
printf("accepted connection: %s\n", buf);
}
struct in_call call;
call.fd = stream;
call.addr = cli_addr;
call_buf_add(call_buf, call);
}
//
// Main
//
void run(struct config cfg) {
char trash[4096];
int client_sock = init_client_sock("/tmp/tel.sock");
int net_sock = init_net_sock(cfg.addr, cfg.addr_len);
struct call_buf call_buf;
call_buf_init(&call_buf);
struct pollfd fds[2 + CALL_BUF_SIZE];
fds[0].fd = client_sock;
fds[0].events = POLLIN;
fds[1].fd = net_sock;
fds[1].events = POLLIN;
while(1) {
int fd_count = 2 + call_buf.len;
for(size_t i = 0; i < call_buf.len; i++) {
fds[i + 2].fd = call_buf.buf[i].fd;
fds[i + 2].events = POLLIN | POLLERR | POLLHUP;
}
int res = poll(fds, fd_count, -1);
if(res < 0) {
perror("error polling");
exit(1);
}
if(fds[0].revents & POLLIN) {
accept_client(&call_buf, fds[0].fd, cfg);
}
if(fds[1].revents & POLLIN) {
accept_net(&call_buf, fds[1].fd);
}
for(int i = 2; i < fd_count; i++) {
if(fds[i].revents & (POLLHUP | POLLHUP)) {
call_buf_remove(&call_buf, i-2);
close(fds[i].fd);
}
if(fds[i].revents & POLLIN) {
read(fds[i].fd, trash, 4096);
}
}
}
}
int main(int argc, char **argv) {
if(argc < 1) return 1;
if(argc < 3) {
fprintf(stderr, "usage: %s <addr> <port>\n", argv[0]);
exit(1);
}
struct config cfg;
cfg.connect_timeout = 1000;
// disable SIGPIPE handler
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGPIPE, &sa, NULL);
// get address from ip/port
char *ip = argv[1];
char *port = argv[2];
int res = tel_get_addr(ip, port, &cfg.addr, &cfg.addr_len);
if(res != 0) {
fprintf(stderr, "error: %s\n", gai_strerror(res));
exit(1);
}
// run application
run(cfg);
return 0;
}