initial commit
This commit is contained in:
commit
19ae6e9dcb
6 changed files with 1048 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
tel
|
||||
teld
|
||||
tel.o
|
||||
teld.o
|
||||
libtel.o
|
25
Makefile
Normal file
25
Makefile
Normal 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
329
libtel.c
Normal 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
41
libtel.h
Normal 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
254
tel.c
Normal 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
394
teld.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue