|
#include <arpa/inet.h> |
|
#include <errno.h> |
|
#include <error.h> |
|
#include <fcntl.h> |
|
#include <netinet/tcp.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <sys/un.h> |
|
#include <time.h> |
|
|
|
//#include "common.h" |
|
|
|
static int net_addr_from_name_inet(struct sockaddr_storage *ss, |
|
const char *host) |
|
{ |
|
struct sockaddr_in *sin = (struct sockaddr_in *)ss; |
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss; |
|
|
|
if (inet_pton(AF_INET, host, &sin->sin_addr) == 1) { |
|
sin->sin_family = AF_INET; |
|
return 0; |
|
} |
|
|
|
if (inet_pton(AF_INET6, host, &sin6->sin6_addr) == 1) { |
|
sin6->sin6_family = AF_INET6; |
|
return 0; |
|
} |
|
return -1; |
|
} |
|
|
|
static int net_addr_from_name_un(struct sockaddr_storage *ss, const char *host) |
|
{ |
|
// AF_UNIX |
|
struct sockaddr_un *sun = (struct sockaddr_un *)ss; |
|
sun->sun_family = AF_UNIX; |
|
strncpy(sun->sun_path, host, sizeof(sun->sun_path)); |
|
if (sun->sun_path[0] == '@') { |
|
// Linux abstract sockets often use @ for zero |
|
sun->sun_path[0] = '\x00'; |
|
} |
|
return 0; |
|
} |
|
|
|
int net_parse_sockaddr(struct sockaddr_storage *ss, const char *addr) |
|
{ |
|
memset(ss, 0, sizeof(struct sockaddr_storage)); |
|
|
|
char *colon = strrchr(addr, ':'); |
|
if (colon == NULL || colon[1] == '\0') { |
|
goto af_unix; |
|
} |
|
|
|
char *endptr; |
|
long port = strtol(&colon[1], &endptr, 10); |
|
if (port < 0 || port > 65535 || *endptr != '\0') { |
|
goto af_unix; |
|
} |
|
|
|
char tmp[255]; |
|
char *host = tmp; |
|
int addr_len = colon - addr > 254 ? 254 : colon - addr; |
|
strncpy(host, addr, addr_len); |
|
host[addr_len] = '\0'; |
|
if (host[0] == '[' && addr_len > 1 && host[addr_len - 1] == ']') { |
|
host[addr_len - 1] = '\0'; |
|
host += 1; |
|
} |
|
int r = net_addr_from_name_inet(ss, host); |
|
if (r != 0) { |
|
af_unix: |
|
net_addr_from_name_un(ss, addr); |
|
} |
|
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)ss; |
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss; |
|
|
|
switch (ss->ss_family) { |
|
case AF_INET: |
|
sin->sin_port = htons(port); |
|
break; |
|
case AF_INET6: |
|
sin6->sin6_port = htons(port); |
|
break; |
|
} |
|
return -1; |
|
} |
|
|
|
#define SOCKADDR_UN_SIZE(sun) \ |
|
((sun)->sun_path[0] == '\x00' \ |
|
? __builtin_offsetof(struct sockaddr_un, sun_path) + 1 + \ |
|
strnlen(&(sun)->sun_path[1], \ |
|
sizeof((sun)->sun_path)) \ |
|
: sizeof(struct sockaddr_un)) |
|
|
|
static size_t sizeof_ss(struct sockaddr_storage *ss) |
|
{ |
|
switch (ss->ss_family) { |
|
case AF_INET: |
|
case AF_INET6: |
|
return sizeof(struct sockaddr_storage); |
|
case AF_UNIX: |
|
return SOCKADDR_UN_SIZE((struct sockaddr_un *)ss); |
|
default: |
|
abort(); |
|
} |
|
} |
|
|
|
int net_connect_blocking(struct sockaddr_storage *ss, int do_zerocopy) |
|
{ |
|
int sd = socket(ss->ss_family, SOCK_STREAM, 0); |
|
if (sd < 0) { |
|
error(-1, errno, "socket()"); |
|
} |
|
|
|
if (ss->ss_family == AF_INET || ss->ss_family == AF_INET6) { |
|
/* Don't buffer partial packets */ |
|
int one = 1; |
|
int r = setsockopt(sd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); |
|
if (r < 0) { |
|
error(-1, errno, "setsockopt()"); |
|
} |
|
|
|
/* Cubic is a bit more stable in tests than bbr */ |
|
char *cong = "cubic"; |
|
r = setsockopt(sd, SOL_TCP, TCP_CONGESTION, cong, strlen(cong)); |
|
if (r < 0) { |
|
error(-1, errno, "setsockopt(TCP_CONGESTION)"); |
|
} |
|
|
|
if (do_zerocopy) { |
|
/* Zerocopy shall be set on the parent accept socket. */ |
|
int one = 1; |
|
int r = setsockopt(sd, SOL_SOCKET, SO_ZEROCOPY, &one, |
|
sizeof(one)); |
|
if (r < 0) { |
|
error(-1, errno, "getsockopt(SO_ZEROCOPY)"); |
|
} |
|
} |
|
} |
|
|
|
again:; |
|
int r = connect(sd, (struct sockaddr *)ss, sizeof_ss(ss)); |
|
if (r < 0) { |
|
if (errno == EINTR) { |
|
goto again; |
|
} |
|
error(-1, errno, "connect()"); |
|
} |
|
|
|
return sd; |
|
} |
|
|
|
int net_getpeername(int sd, struct sockaddr_storage *ss) |
|
{ |
|
memset(ss, 0, sizeof(struct sockaddr_storage)); |
|
|
|
socklen_t ss_len = sizeof(struct sockaddr_storage); |
|
int r = getpeername(sd, (struct sockaddr *)ss, &ss_len); |
|
if (r < 0) { |
|
error(-1, errno, "getpeername()"); |
|
} |
|
|
|
/* stick trailing zero to AF_UNIX name */ |
|
if (ss_len < sizeof(struct sockaddr_storage)) { |
|
((char *)ss)[ss_len] = '\0'; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int net_getsockname(int sd, struct sockaddr_storage *ss) |
|
{ |
|
memset(ss, 0, sizeof(struct sockaddr_storage)); |
|
|
|
socklen_t ss_len = sizeof(struct sockaddr_storage); |
|
int r = getsockname(sd, (struct sockaddr *)ss, &ss_len); |
|
if (r < 0) { |
|
error(-1, errno, "getsockname()"); |
|
} |
|
|
|
/* stick trailing zero to AF_UNIX name */ |
|
if (ss_len < sizeof(struct sockaddr_storage)) { |
|
((char *)ss)[ss_len] = '\0'; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
const char *net_ntop(struct sockaddr_storage *ss) |
|
{ |
|
char s[sizeof(struct sockaddr_storage) + 1]; |
|
static char a[sizeof(struct sockaddr_storage) + 32]; |
|
struct sockaddr_in *sin = (struct sockaddr_in *)ss; |
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss; |
|
struct sockaddr_un *sun = (struct sockaddr_un *)ss; |
|
int port; |
|
const char *r; |
|
switch (ss->ss_family) { |
|
case AF_INET: |
|
port = htons(sin->sin_port); |
|
r = inet_ntop(sin->sin_family, &sin->sin_addr, s, sizeof(s)); |
|
if (r == NULL) { |
|
error(-1, errno, "inet_ntop()"); |
|
} |
|
snprintf(a, sizeof(a), "inet://%s:%i", s, port); |
|
break; |
|
case AF_INET6: |
|
r = inet_ntop(sin6->sin6_family, &sin6->sin6_addr, s, |
|
sizeof(s)); |
|
if (r == NULL) { |
|
error(-1, errno, "inet_ntop()"); |
|
} |
|
port = htons(sin6->sin6_port); |
|
snprintf(a, sizeof(a), "inet6://[%s]:%i", s, port); |
|
break; |
|
case AF_UNIX: |
|
memcpy(s, sun->sun_path, sizeof(sun->sun_path)); |
|
s[sizeof(sun->sun_path)] = '\x00'; |
|
if (s[0] == '\x00') { |
|
s[0] = '@'; |
|
} |
|
snprintf(a, sizeof(a), "unix://%s", s); |
|
break; |
|
default: |
|
abort(); |
|
} |
|
return a; |
|
} |
|
|
|
int net_bind(struct sockaddr_storage *ss) |
|
{ |
|
int sd = socket(ss->ss_family, SOCK_STREAM, 0); |
|
if (sd < 0) { |
|
error(-1, errno, "socket()"); |
|
} |
|
|
|
int one = 1; |
|
int r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, |
|
sizeof(one)); |
|
if (r < 0) { |
|
error(-1, errno, "setsockopt(SO_REUSEADDR)"); |
|
} |
|
|
|
if (ss->ss_family == AF_INET || ss->ss_family == AF_INET6) { |
|
one = 1; |
|
r = setsockopt(sd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); |
|
if (r < 0) { |
|
error(-1, errno, "setsockopt()"); |
|
} |
|
|
|
/* Cubic is a bit more stable in tests than bbr */ |
|
char *cong = "cubic"; |
|
r = setsockopt(sd, SOL_TCP, TCP_CONGESTION, cong, strlen(cong)); |
|
if (r < 0) { |
|
error(-1, errno, "setsockopt(TCP_CONGESTION)"); |
|
} |
|
} |
|
|
|
if (ss->ss_family == AF_INET6) { |
|
one = 1; |
|
r = setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &one, |
|
sizeof(one)); |
|
if (r < 0) { |
|
error(-1, errno, "setsockopt(IPV6_V6ONLY)"); |
|
} |
|
} |
|
|
|
r = bind(sd, (struct sockaddr *)ss, sizeof_ss(ss)); |
|
if (r < 0) { |
|
error(-1, errno, "bind()"); |
|
} |
|
|
|
listen(sd, 1024); |
|
return sd; |
|
} |
|
|
|
int net_accept(int sd, struct sockaddr_storage *ss) |
|
{ |
|
again_accept:; |
|
socklen_t ss_len = sizeof(struct sockaddr_storage); |
|
int cd = accept(sd, (struct sockaddr *)ss, &ss_len); |
|
if (cd < 0) { |
|
if (errno == EINTR) { |
|
goto again_accept; |
|
} |
|
error(-1, errno, "accept()"); |
|
} |
|
|
|
/* stick trailing zero to AF_UNIX name */ |
|
if (ss_len < sizeof(struct sockaddr_storage)) { |
|
((char *)ss)[ss_len] = '\0'; |
|
} |
|
return cd; |
|
} |
|
|
|
void set_nonblocking(int fd) |
|
{ |
|
int flags, ret; |
|
flags = fcntl(fd, F_GETFL, 0); |
|
if (-1 == flags) { |
|
flags = 0; |
|
} |
|
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK); |
|
if (-1 == ret) { |
|
error(-1, errno, "fcntl(O_NONBLOCK)"); |
|
} |
|
} |