/* * Networking library * * (C) France Telecom R&D * Yoann Guillot */ #include #include #include #include #include #include #include #include #include #include "net.h" /* default timeout: 2mn */ static unsigned long connect_timeout = 120*1000; /* * set the timeout value for the timer used when connect()ing * 0 means infinity */ void socket_set_connect_timeout(unsigned long msec) { connect_timeout = msec; } /* * connect() returned EINPROGRESS: wait for success */ static int socket_connect_check(int sock) { fd_set wr; struct timeval tv; int ret; for (;;) { tv.tv_sec = connect_timeout / 1000; tv.tv_usec = (connect_timeout % 1000) * 1000; FD_ZERO(&wr); FD_SET(sock, &wr); ret = select(sock+1, 0, &wr, 0, &tv); if (!ret) { /* timeout */ errno = ETIMEDOUT; return 1; } if (ret == -1 && errno != EAGAIN && errno != EINTR) return 1; if (ret > 0) { if (!FD_ISSET(sock, &wr)) return 1; int e; socklen_t l = sizeof(e); ret = getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *)&e, &l); if (!ret && !e) return 0; errno = e; return 1; } } } /* * create an ip socket, connect it to server:port * the socket is blocking, it is just made nonblocking to allow control of the * timeout. */ int socket_connect(char *server, char *port) { int sock; struct addrinfo hints, *ai_head, *ai_tmp; int ret; memset(&hints, 0, sizeof(hints)); hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; for (;;) { ret = getaddrinfo(server, port, &hints, &ai_head); if (!ret) break; if (ret != EAI_SYSTEM) { errno = EADDRNOTAVAIL; return -1; } if (errno != EAGAIN && errno != EINTR) return -1; } /* try all adresses fetched by getaddrinfo() */ for (ai_tmp=ai_head ; ai_tmp ; ai_tmp=ai_tmp->ai_next) { sock = socket(ai_tmp->ai_family, ai_tmp->ai_socktype, ai_tmp->ai_protocol); if (sock != -1) { long sockfl; sockfl = fcntl(sock, F_GETFL); fcntl(sock, F_SETFL, O_NONBLOCK); for (;;) { ret = connect(sock, ai_tmp->ai_addr, ai_tmp->ai_addrlen); if (ret != -1 || (errno == EINPROGRESS && !socket_connect_check(sock))) { /* success */ freeaddrinfo(ai_head); if (sockfl != -1) fcntl(sock, F_SETFL, sockfl); return sock; } else if (errno != EAGAIN && errno != EINTR) break; } ret = errno; socket_close(sock); errno = ret; } } ret = errno; freeaddrinfo(ai_head); errno = ret; return -1; } /* * creates a listening ip socket, on server:port * socket is nonblocking */ int socket_listen(char *server, char *port) { int sock; struct addrinfo hints, *ai_head, *ai_tmp; int ret; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; for (;;) { ret = getaddrinfo(server, port, &hints, &ai_head); if (!ret) break; if (ret != EAI_SYSTEM) { errno = EADDRNOTAVAIL; return -1; } if (errno != EAGAIN && errno != EINTR) return -1; } for (ai_tmp=ai_head ; ai_tmp ; ai_tmp=ai_tmp->ai_next) { sock = socket(ai_tmp->ai_family, ai_tmp->ai_socktype, ai_tmp->ai_protocol); if (sock != -1) { ret = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &ret, sizeof(ret)); if (bind(sock, ai_tmp->ai_addr, ai_tmp->ai_addrlen) != -1 && listen(sock, 4) != -1) { freeaddrinfo(ai_head); fcntl(sock, F_SETFL, O_NONBLOCK); return sock; } ret = errno; socket_close(sock); errno = ret; } } ret = errno; freeaddrinfo(ai_head); errno = ret; return -1; } /* * accepts an incoming connection on a socket returned by socket_listen() * returns the new socket, which is blocking. * It also returns the peer adress in the parameters (set to NULL to ignore) * The peer adress is not resolved, it is numeric (eg. "127.0.0.1", "3ffe::5"..) */ int socket_accept(int sock, char *clienth, size_t clienth_len, char *clientp, size_t clientp_len) { struct sockaddr sa; socklen_t salen; int nsock; int ret; for (;;) { salen = sizeof(sa); nsock = accept(sock, &sa, &salen); if (nsock != -1) break; if (errno != EINTR) return -1; } for (;;) { ret = getnameinfo(&sa, salen, clienth, clienth_len, clientp, clientp_len, NI_NUMERICHOST | NI_NUMERICSERV); if (!ret) break; if (ret != EAI_SYSTEM || (errno != EINTR && errno != EAGAIN)) { if (clienth && clienth_len > 0) *clienth = 0; if (clientp && clientp_len > 0) *clientp = 0; break; } } return nsock; } /* * terminates the stream using the socket * (coming either from socket_accept, socket_connect or socket_listen) */ void socket_close(int sock) { shutdown(sock, SHUT_RDWR); while (close(sock) == -1 && errno == EINTR); }