xs_socket.h (5691B)
1 /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ 2 3 #ifndef _XS_SOCKET_H 4 5 #define _XS_SOCKET_H 6 7 int xs_socket_timeout(int s, double rto, double sto); 8 int xs_socket_server(const char *addr, const char *serv); 9 int xs_socket_accept(int rs); 10 int _xs_socket_peername(int s, char *buf, int buf_size); 11 int xs_socket_connect(const char *addr, const char *serv); 12 13 #ifdef _XS_H 14 xs_str *xs_socket_peername(int s); 15 #endif 16 17 18 #ifdef XS_IMPLEMENTATION 19 20 #include <sys/socket.h> 21 #include <netdb.h> 22 #include <netinet/in.h> 23 #include <arpa/inet.h> 24 #include <string.h> 25 #include <stdlib.h> 26 #include <unistd.h> 27 28 29 int xs_socket_timeout(int s, double rto, double sto) 30 /* sets the socket timeout in seconds */ 31 { 32 struct timeval tv; 33 int ret = 0; 34 35 if (rto > 0.0) { 36 tv.tv_sec = (int)rto; 37 tv.tv_usec = (int)((rto - (double)(int)rto) * 1000000.0); 38 39 ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); 40 } 41 42 if (sto > 0.0) { 43 tv.tv_sec = (int)sto; 44 tv.tv_usec = (int)((sto - (double)(int)sto) * 1000000.0); 45 46 ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)); 47 } 48 49 return ret; 50 } 51 52 53 int xs_socket_server(const char *addr, const char *serv) 54 /* opens a server socket by service name (or port as string) */ 55 { 56 int rs = -1; 57 #ifndef WITHOUT_GETADDRINFO 58 struct addrinfo *res; 59 struct addrinfo hints; 60 int status; 61 62 memset(&hints, '\0', sizeof(hints)); 63 64 hints.ai_family = AF_UNSPEC; 65 hints.ai_socktype = SOCK_STREAM; 66 67 if (getaddrinfo(addr, serv, &hints, &res) != 0) { 68 goto end; 69 } 70 71 rs = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 72 73 /* reuse addr */ 74 int i = 1; 75 setsockopt(rs, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i)); 76 77 status = bind(rs, res->ai_addr, res->ai_addrlen); 78 if (status == -1) { 79 fprintf(stderr, "Error binding with status %d\n", status); 80 close(rs); 81 rs = -1; 82 } 83 else { 84 85 listen(rs, SOMAXCONN); 86 } 87 88 freeaddrinfo(res); 89 90 #else /* WITHOUT_GETADDRINFO */ 91 struct sockaddr_in host; 92 93 memset(&host, '\0', sizeof(host)); 94 95 if (addr != NULL) { 96 struct hostent *he; 97 98 if ((he = gethostbyname(addr)) != NULL) 99 memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length); 100 else 101 goto end; 102 } 103 104 struct servent *se; 105 106 if ((se = getservbyname(serv, "tcp")) != NULL) 107 host.sin_port = se->s_port; 108 else 109 host.sin_port = htons(atoi(serv)); 110 111 host.sin_family = AF_INET; 112 113 if ((rs = socket(AF_INET, SOCK_STREAM, 0)) != -1) { 114 /* reuse addr */ 115 int i = 1; 116 setsockopt(rs, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i)); 117 118 if (bind(rs, (struct sockaddr *)&host, sizeof(host)) == -1) { 119 close(rs); 120 rs = -1; 121 } 122 else 123 listen(rs, SOMAXCONN); 124 } 125 126 #endif /* WITHOUT_GETADDRINFO */ 127 end: 128 return rs; 129 } 130 131 132 int xs_socket_accept(int rs) 133 /* accepts an incoming connection */ 134 { 135 struct sockaddr_storage addr; 136 socklen_t l = sizeof(addr); 137 138 return accept(rs, (struct sockaddr *)&addr, &l); 139 } 140 141 142 int _xs_socket_peername(int s, char *buf, int buf_size) 143 /* fill the buffer with the socket peername */ 144 { 145 struct sockaddr_storage addr; 146 socklen_t slen = sizeof(addr); 147 const char *p = NULL; 148 149 if (getpeername(s, (struct sockaddr *)&addr, &slen) != -1) { 150 if (addr.ss_family == AF_INET) { 151 struct sockaddr_in *sa = (struct sockaddr_in *)&addr; 152 153 p = inet_ntop(AF_INET, &sa->sin_addr, buf, buf_size); 154 } 155 else 156 if (addr.ss_family == AF_INET6) { 157 struct sockaddr_in6 *sa = (struct sockaddr_in6 *)&addr; 158 159 p = inet_ntop(AF_INET6, &sa->sin6_addr, buf, buf_size); 160 } 161 } 162 163 return p != NULL; 164 } 165 166 167 int xs_socket_connect(const char *addr, const char *serv) 168 /* creates a client connection socket */ 169 { 170 int d = -1; 171 172 #ifndef WITHOUT_GETADDRINFO 173 struct addrinfo *res; 174 struct addrinfo hints; 175 176 memset(&hints, '\0', sizeof(hints)); 177 178 hints.ai_socktype = SOCK_STREAM; 179 hints.ai_flags = AI_ADDRCONFIG; 180 181 if (getaddrinfo(addr, serv, &hints, &res) == 0) { 182 struct addrinfo *r; 183 184 for (r = res; r != NULL; r = r->ai_next) { 185 d = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 186 187 if (d != -1) { 188 if (connect(d, r->ai_addr, r->ai_addrlen) == 0) 189 break; 190 191 close(d); 192 d = -1; 193 } 194 } 195 196 freeaddrinfo(res); 197 } 198 199 #else /* WITHOUT_GETADDRINFO */ 200 201 /* traditional socket interface */ 202 struct hostent *he; 203 204 if ((he = gethostbyname(addr)) != NULL) { 205 struct sockaddr_in host; 206 207 memset(&host, '\0', sizeof(host)); 208 209 memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length); 210 host.sin_family = he->h_addrtype; 211 212 struct servent *se; 213 214 if ((se = getservbyname(serv, "tcp")) != NULL) 215 host.sin_port = se->s_port; 216 else 217 host.sin_port = htons(atoi(serv)); 218 219 if ((d = socket(AF_INET, SOCK_STREAM, 0)) != -1) { 220 if (connect(d, (struct sockaddr *)&host, sizeof(host)) == -1) { 221 close(d); 222 d = -1; 223 } 224 } 225 } 226 227 #endif /* WITHOUT_GETADDRINFO */ 228 229 return d; 230 } 231 232 233 #ifdef _XS_H 234 235 xs_str *xs_socket_peername(int s) 236 /* returns the remote address as a string */ 237 { 238 char buf[2028]; 239 xs_str *p = NULL; 240 241 if (_xs_socket_peername(s, buf, sizeof(buf))) 242 p = xs_str_new(buf); 243 244 return p; 245 } 246 247 #endif /* _XS_H */ 248 249 #endif /* XS_IMPLEMENTATION */ 250 251 #endif /* _XS_SOCKET_H */