diff --git a/HISTORY b/HISTORY index f7dd1f6..0a6cecb 100644 --- a/HISTORY +++ b/HISTORY @@ -1,9 +1,10 @@ -Version 1.41 2019-09-22 +Version 1.41 2019-09-27 * change CIDR network_bits range from [16, 32) to [10, 32) * ini_file_reader.c: fix empty string compare * multi_socket_client.c: code refine + * sockopt.[hc] support IPv6 Version 1.40 2018-11-09 * add function conn_pool_parse_server_info and conn_pool_load_server_info diff --git a/src/sockopt.c b/src/sockopt.c index a2e0c23..a2b0a16 100644 --- a/src/sockopt.c +++ b/src/sockopt.c @@ -462,35 +462,35 @@ int tcpsenddata_nb(int sock, void* data, const int size, const int timeout) return 0; } -int setsockaddrbyip(const char *ip, const short port, struct sockaddr_in *addr, - struct sockaddr_in6 *addr6, void **output, int *size) +int setsockaddrbyip(const char *ip, const short port, sockaddr_convert_t *convert) { - int domain; + int af; void *dest; if (is_ipv6_addr(ip)) { - *output = addr6; - *size = sizeof(*addr6); - dest = &addr6->sin6_addr; + convert->len = sizeof(convert->sa.addr6); + dest = &convert->sa.addr6.sin6_addr; - domain = AF_INET6; - addr6->sin6_family = PF_INET6; - addr6->sin6_port = htons(port); + af = AF_INET6; + convert->sa.addr6.sin6_family = PF_INET6; + convert->sa.addr6.sin6_port = htons(port); } else //ipv4 { - *output = addr; - *size = sizeof(*addr); - dest = &addr->sin_addr; + convert->len = sizeof(convert->sa.addr4); + dest = &convert->sa.addr4.sin_addr; - domain = AF_INET; - addr->sin_family = PF_INET; - addr->sin_port = htons(port); + af = AF_INET; + convert->sa.addr4.sin_family = PF_INET; + convert->sa.addr4.sin_port = htons(port); } - if (inet_pton(domain, ip, dest) == 0) + if (inet_pton(af, ip, dest) == 0) { + logError("file: "__FILE__", line: %d, " + "invalid %s ip address: %s", __LINE__, + (af == AF_INET ? "IPv4" : "IPv6"), ip); return EINVAL; } return 0; @@ -499,18 +499,14 @@ int setsockaddrbyip(const char *ip, const short port, struct sockaddr_in *addr, int connectserverbyip(int sock, const char *server_ip, const short server_port) { int result; - struct sockaddr_in addr; - struct sockaddr_in6 addr6; - void *dest; - int size; + sockaddr_convert_t convert; - if ((result=setsockaddrbyip(server_ip, server_port, &addr, &addr6, - &dest, &size)) != 0) + if ((result=setsockaddrbyip(server_ip, server_port, &convert)) != 0) { return result; } - if (connect(sock, (const struct sockaddr*)dest, size) < 0) + if (connect(sock, &convert.sa.addr, convert.len) < 0) { return errno != 0 ? errno : EINTR; } @@ -535,13 +531,9 @@ int connectserverbyip_nb_ex(int sock, const char *server_ip, \ struct pollfd pollfds; #endif - struct sockaddr_in addr; - struct sockaddr_in6 addr6; - void *dest; - int size; + sockaddr_convert_t convert; - if ((result=setsockaddrbyip(server_ip, server_port, &addr, &addr6, - &dest, &size)) != 0) + if ((result=setsockaddrbyip(server_ip, server_port, &convert)) != 0) { return result; } @@ -576,7 +568,7 @@ int connectserverbyip_nb_ex(int sock, const char *server_ip, \ do { - if (connect(sock, (const struct sockaddr*)dest, size) < 0) + if (connect(sock, &convert.sa.addr, convert.len) < 0) { result = errno != 0 ? errno : EINPROGRESS; if (result != EINPROGRESS) @@ -634,16 +626,99 @@ int connectserverbyip_nb_ex(int sock, const char *server_ip, \ return result; } +int socketClientEx2(int af, const char *server_ip, + const short server_port, const int timeout, + const int flags, const char *bind_ipaddr, int *err_no) +{ + int sock; + bool auto_detect; + + if (af == AF_UNSPEC) + { + af = is_ipv6_addr(server_ip) ? AF_INET6 : AF_INET; + } + + sock = socket(af, SOCK_STREAM, 0); + if (sock < 0) + { + *err_no = errno != 0 ? errno : EMFILE; + logError("file: "__FILE__", line: %d, " \ + "socket create failed, errno: %d, error info: %s", \ + __LINE__, errno, STRERROR(errno)); + return -1; + } + + if (flags != 0) + { + *err_no = fd_add_flags(sock, flags); + if (*err_no != 0) + { + close(sock); + return -2; + } + } + + if (bind_ipaddr != NULL && *bind_ipaddr != '\0') + { + *err_no = socketBind2(af, sock, bind_ipaddr, 0); + if (*err_no != 0) + { + close(sock); + return -3; + } + } + + auto_detect = ((flags & O_NONBLOCK) == 0); + *err_no = connectserverbyip_nb_ex(sock, server_ip, + server_port, timeout, auto_detect); + if (*err_no != 0) + { + close(sock); + return -4; + } + + return sock; +} + +const char * fc_inet_ntop(const struct sockaddr *addr, + char *buff, const int bufferSize) +{ + void *sin_addr; + const char *output; + + if (addr->sa_family == AF_INET) { + sin_addr = &((struct sockaddr_in *)addr)->sin_addr; + } else if (addr->sa_family == AF_INET6) { + sin_addr = &((struct sockaddr_in6 *)addr)->sin6_addr; + } else { + *buff = '\0'; + logWarning("file: "__FILE__", line: %d, " + "unkown family: %d", __LINE__, addr->sa_family); + return NULL; + } + + if ((output=inet_ntop(addr->sa_family, sin_addr, buff, bufferSize)) == NULL) + { + *buff = '\0'; + logWarning("file: "__FILE__", line: %d, " + "call inet_ntop fail, " + "errno: %d, error info: %s", + __LINE__, errno, STRERROR(errno)); + } + + return output; +} + in_addr_t getIpaddr(getnamefunc getname, int sock, \ char *buff, const int bufferSize) { - struct sockaddr_in addr; + struct sockaddr addr; socklen_t addrlen; memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); - if (getname(sock, (struct sockaddr *)&addr, &addrlen) != 0) + if (getname(sock, &addr, &addrlen) != 0) { *buff = '\0'; return INADDR_NONE; @@ -651,31 +726,29 @@ in_addr_t getIpaddr(getnamefunc getname, int sock, \ if (addrlen > 0) { - if (inet_ntop(AF_INET, &addr.sin_addr, buff, bufferSize) == NULL) - { - *buff = '\0'; - } + fc_inet_ntop(&addr, buff, bufferSize); } else { *buff = '\0'; } - return addr.sin_addr.s_addr; + return ((struct sockaddr_in *)&addr)->sin_addr.s_addr; //DO NOT support IPv6 } char *getHostnameByIp(const char *szIpAddr, char *buff, const int bufferSize) { - struct in_addr ip_addr; struct hostent *ent; + sockaddr_convert_t convert; - if (inet_pton(AF_INET, szIpAddr, &ip_addr) != 1) - { + if (setsockaddrbyip(szIpAddr, 0, &convert) != 0) + { *buff = '\0'; return buff; - } + } - ent = gethostbyaddr((char *)&ip_addr, sizeof(ip_addr), AF_INET); + ent = gethostbyaddr(&convert.sa.addr, convert.len, + convert.sa.addr.sa_family); if (ent == NULL || ent->h_name == NULL) { *buff = '\0'; @@ -839,32 +912,40 @@ int nbaccept(int sock, const int timeout, int *err_no) return result; } -int socketBind(int sock, const char *bind_ipaddr, const int port) +int socketBind2(int af, int sock, const char *bind_ipaddr, const int port) { - struct sockaddr_in bindaddr; + sockaddr_convert_t convert; + int result; - bindaddr.sin_family = AF_INET; - bindaddr.sin_port = htons(port); + convert.sa.addr.sa_family = af; if (bind_ipaddr == NULL || *bind_ipaddr == '\0') { - bindaddr.sin_addr.s_addr = INADDR_ANY; + if (af == AF_INET) + { + convert.len = sizeof(convert.sa.addr4); + convert.sa.addr4.sin_port = htons(port); + convert.sa.addr4.sin_addr.s_addr = INADDR_ANY; + } + else + { + convert.len = sizeof(convert.sa.addr6); + convert.sa.addr6.sin6_port = htons(port); + convert.sa.addr6.sin6_addr = in6addr_any; + } } else - { - if (inet_pton(AF_INET, bind_ipaddr, &bindaddr.sin_addr) == 0) - { - logError("file: "__FILE__", line: %d, " \ - "invalid ip addr %s", \ - __LINE__, bind_ipaddr); - return EINVAL; - } - } + { + if ((result=setsockaddrbyip(bind_ipaddr, port, &convert)) != 0) + { + return result; + } + } - if (bind(sock, (struct sockaddr*)&bindaddr, sizeof(bindaddr)) < 0) + if (bind(sock, &convert.sa.addr, convert.len) < 0) { - logError("file: "__FILE__", line: %d, " \ - "bind port %d failed, " \ - "errno: %d, error info: %s.", \ + logError("file: "__FILE__", line: %d, " + "bind port %d failed, " + "errno: %d, error info: %s.", __LINE__, port, errno, STRERROR(errno)); return errno != 0 ? errno : ENOMEM; } @@ -872,12 +953,22 @@ int socketBind(int sock, const char *bind_ipaddr, const int port) return 0; } -int socketServer(const char *bind_ipaddr, const int port, int *err_no) +int socketBind(int sock, const char *bind_ipaddr, const int port) +{ + return socketBind2(AF_INET, sock, bind_ipaddr, port); +} + +int socketBindIPv6(int sock, const char *bind_ipaddr, const int port) +{ + return socketBind2(AF_INET6, sock, bind_ipaddr, port); +} + +int socketServer2(int af, const char *bind_ipaddr, const int port, int *err_no) { int sock; int result; - sock = socket(AF_INET, SOCK_STREAM, 0); + sock = socket(af, SOCK_STREAM, 0); if (sock < 0) { *err_no = errno != 0 ? errno : EMFILE; @@ -900,7 +991,7 @@ int socketServer(const char *bind_ipaddr, const int port, int *err_no) return -2; } - if ((*err_no=socketBind(sock, bind_ipaddr, port)) != 0) + if ((*err_no=socketBind2(af, sock, bind_ipaddr, port)) != 0) { close(sock); return -3; @@ -921,6 +1012,16 @@ int socketServer(const char *bind_ipaddr, const int port, int *err_no) return sock; } +int socketServer(const char *bind_ipaddr, const int port, int *err_no) +{ + return socketServer2(AF_INET, bind_ipaddr, port, err_no); +} + +int socketServerIPv6(const char *bind_ipaddr, const int port, int *err_no) +{ + return socketServer2(AF_INET6, bind_ipaddr, port, err_no); +} + int tcprecvfile(int sock, const char *filename, const int64_t file_bytes, \ const int fsync_after_written_bytes, const int timeout, \ int64_t *true_file_bytes) diff --git a/src/sockopt.h b/src/sockopt.h index 9b0b634..aad33e2 100644 --- a/src/sockopt.h +++ b/src/sockopt.h @@ -32,6 +32,15 @@ typedef struct ip_addr_s { int socket_domain; } ip_addr_t; +typedef struct sockaddr_convert_s { + socklen_t len; + union { + struct sockaddr addr; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + } sa; +} sockaddr_convert_t; + #ifdef SO_NOSIGPIPE #define SET_SOCKOPT_NOSIGPIPE(sock) \ do { \ @@ -238,7 +247,7 @@ in_addr_t getIpaddr(getnamefunc getname, int sock, \ */ char *getHostnameByIp(const char *szIpAddr, char *buff, const int bufferSize); -/** get by ip address by it's hostname +/** get by IPv4 address by it's hostname * parameters: * name: the hostname * buff: buffer to store the ip address @@ -256,7 +265,7 @@ in_addr_t getIpaddrByName(const char *name, char *buff, const int bufferSize); */ int getIpaddrsByName(const char *name, ip_addr_t *ip_addr_arr, const int ip_addr_arr_size); -/** bind wrapper +/** bind wrapper for IPv4 * parameters: * sock: the socket * bind_ipaddr: the ip address to bind @@ -265,7 +274,26 @@ int getIpaddrsByName(const char *name, ip_addr_t *ip_addr_arr, const int ip_addr */ int socketBind(int sock, const char *bind_ipaddr, const int port); -/** start a socket server (socket, bind and listen) +/** bind wrapper for IPv6 + * parameters: + * sock: the socket + * bind_ipaddr: the ip address to bind + * port: the port to bind + * return: error no, 0 success, != 0 fail +*/ +int socketBindIPv6(int sock, const char *bind_ipaddr, const int port); + +/** bind wrapper for IPv4 or IPv6 + * parameters: + * af: family, AF_INET or AF_INET6 + * sock: the socket + * bind_ipaddr: the ip address to bind + * port: the port to bind + * return: error no, 0 success, != 0 fail +*/ +int socketBind2(int af, int sock, const char *bind_ipaddr, const int port); + +/** start a socket server for IPv4 (socket, bind and listen) * parameters: * sock: the socket * bind_ipaddr: the ip address to bind @@ -275,6 +303,130 @@ int socketBind(int sock, const char *bind_ipaddr, const int port); */ int socketServer(const char *bind_ipaddr, const int port, int *err_no); +/** start a socket server for IPv6 (socket, bind and listen) + * parameters: + * sock: the socket + * bind_ipaddr: the ip address to bind + * port: the port to bind + * err_no: store the error no + * return: >= 0 server socket, < 0 fail +*/ +int socketServerIPv6(const char *bind_ipaddr, const int port, int *err_no); + +/** start a socket server for IPv4 or IPv6 (socket, bind and listen) + * parameters: + * af: family, AF_INET or AF_INET6 + * sock: the socket + * bind_ipaddr: the ip address to bind + * port: the port to bind + * err_no: store the error no + * return: >= 0 server socket, < 0 fail +*/ +int socketServer2(int af, const char *bind_ipaddr, const int port, int *err_no); + +/** connect to server + * parameters: + * af: family, AF_UNSPEC (auto dectect), AF_INET or AF_INET6 + * server_ip: ip address of the server + * server_port: port of the server + * timeout: connect timeout in seconds + * flags: socket flags such as O_NONBLOCK for non-block socket + * bind_ipaddr: the ip address to bind, NULL or empty for bind ANY + * err_no: store the error no + * return: >= 0 server socket, < 0 fail +*/ +int socketClientEx2(int af, const char *server_ip, + const short server_port, const int timeout, + const int flags, const char *bind_ipaddr, int *err_no); + +/** connect to server + * parameters: + * server_ip: ip address of the server + * server_port: port of the server + * timeout: connect timeout in seconds + * flags: socket flags such as O_NONBLOCK for non-block socket + * bind_ipaddr: the ip address to bind, NULL or empty for bind ANY + * err_no: store the error no + * return: >= 0 server socket, < 0 fail +*/ +static inline int socketClientExAuto(const char *server_ip, + const short server_port, const int timeout, + const int flags, const char *bind_ipaddr, int *err_no) +{ + return socketClientEx2(AF_UNSPEC, server_ip, server_port, + timeout, flags, bind_ipaddr, err_no); +} + +/** connect to server + * parameters: + * server_ip: ip address of the server + * server_port: port of the server + * timeout: connect timeout in seconds + * flags: socket flags such as O_NONBLOCK for non-block socket + * bind_ipaddr: the ip address to bind, NULL or empty for bind ANY + * err_no: store the error no + * return: >= 0 server socket, < 0 fail +*/ +static inline int socketClientAuto(const char *server_ip, + const short server_port, const int timeout, + const int flags, int *err_no) +{ + return socketClientEx2(AF_UNSPEC, server_ip, server_port, + timeout, flags, NULL, err_no); +} + +/** connect to server + * parameters: + * af: family, AF_UNSPEC (auto dectect), AF_INET or AF_INET6 + * server_ip: ip address of the server + * server_port: port of the server + * timeout: connect timeout in seconds + * flags: socket flags such as O_NONBLOCK for non-block socket + * err_no: store the error no + * return: >= 0 server socket, < 0 fail +*/ +static inline int socketClient2(int af, const char *server_ip, + const short server_port, const int timeout, + const int flags, int *err_no) +{ + return socketClientEx2(af, server_ip, server_port, + timeout, flags, NULL, err_no); +} + +/** connect to server with IPv4 socket + * parameters: + * server_ip: ip address of the server + * server_port: port of the server + * timeout: connect timeout in seconds + * flags: socket flags such as O_NONBLOCK for non-block socket + * err_no: store the error no + * return: >= 0 server socket, < 0 fail +*/ +static inline int socketClient(const char *server_ip, + const short server_port, const int timeout, + const int flags, int *err_no) +{ + return socketClient2(AF_INET, server_ip, server_port, + timeout, flags, err_no); +} + +/** connect to server with IPv6 socket + * parameters: + * server_ip: ip address of the server + * server_port: port of the server + * timeout: connect timeout in seconds + * flags: socket flags such as O_NONBLOCK for non-block socket + * err_no: store the error no + * return: >= 0 server socket, < 0 fail +*/ +static inline int socketClientIPv6(const char *server_ip, + const short server_port, const int timeout, + const int flags, int *err_no) +{ + return socketClient2(AF_INET6, server_ip, server_port, + timeout, flags, err_no); +} + #define tcprecvdata(sock, data, size, timeout) \ tcprecvdata_ex(sock, data, size, timeout, NULL) @@ -374,18 +526,14 @@ int gethostaddrs(char **if_alias_prefixes, const int prefix_count, \ */ int getifconfigs(FastIFConfig *if_configs, const int max_count, int *count); -/** set socket address by ip +/** set socket address by ip and port * parameters: * ip: the ip address * port: the port - * addr: ipv4 addr - * addr6: ipv6 addr - * output: return addr pointer - * size: return the size of addr + * convert: the convert struct for IPv4 and IPv6 compatibility * return: error no, 0 success, != 0 fail */ -int setsockaddrbyip(const char *ip, const short port, struct sockaddr_in *addr, - struct sockaddr_in6 *addr6, void **output, int *size); +int setsockaddrbyip(const char *ip, const short port, sockaddr_convert_t *convert); static inline bool is_ipv6_addr(const char *ip) {