tel/libtel.c

332 lines
7.1 KiB
C

#include <arpa/inet.h>
#include <stdlib.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 = malloc(info->ai_addrlen);
*addr_len = info->ai_addrlen;
memcpy(*addr, info->ai_addr, 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;
}