1 /*
2  * apclibnis.c
3  *
4  * Network utility routines.
5  */
6 
7 /*
8  * Copyright (C) 1999-2006 Kern Sibbald
9  * Copyright (C) 2007-2015 Adam Kropelin
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU General
13  * Public License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public
21  * License along with this program; if not, write to the Free
22  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23  * MA 02110-1335, USA.
24  */
25 
26 #include "apc.h"
27 
28 #ifdef HAVE_NISLIB
29 
30 /* Some Win32 specific screwery */
31 #ifdef HAVE_MINGW
32 
33 #define close(fd)             closesocket(fd)
34 #define getsockopt(s,l,o,d,z) getsockopt((s),(l),(o),(char*)(d),(z))
35 #define EINPROGRESS           WSAEWOULDBLOCK
36 
37 int WSA_Init(void);
38 int dummy = WSA_Init();
39 
40 #undef errno
41 #define errno   WSAGetLastError()
42 
43 #undef h_errno
44 #define h_errno WSAGetLastError()
45 
46 #endif // HAVE_MINGW
47 
48 
49 /*
50  * Read nbytes from the network.
51  * It is possible that the total bytes require in several
52  * read requests
53  */
54 
read_nbytes(sock_t fd,char * ptr,int nbytes)55 static int read_nbytes(sock_t fd, char *ptr, int nbytes)
56 {
57    int nleft, nread = 0;
58    struct timeval timeout;
59    int rc;
60    fd_set fds;
61 
62    nleft = nbytes;
63 
64    while (nleft > 0) {
65       do {
66          /* Expect data from the server within 15 seconds */
67          timeout.tv_sec = 15;
68          timeout.tv_usec = 0;
69 
70          FD_ZERO(&fds);
71          FD_SET(fd, &fds);
72 
73          rc = select(fd + 1, &fds, NULL, NULL, &timeout);
74 
75          switch (rc) {
76          case -1:
77             if (errno == EINTR || errno == EAGAIN)
78                continue;
79             return -errno;       /* error */
80          case 0:
81             return -ETIMEDOUT;   /* timeout */
82          }
83 
84          nread = recv(fd, ptr, nleft, 0);
85       } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
86 
87       if (nread == 0)
88          return 0;               /* EOF */
89       if (nread < 0)
90          return -errno;          /* error */
91 
92       nleft -= nread;
93       ptr += nread;
94    }
95 
96    return nbytes - nleft;        /* return >= 0 */
97 }
98 
99 /*
100  * Write nbytes to the network.
101  * It may require several writes.
102  */
write_nbytes(sock_t fd,const char * ptr,int nbytes)103 static int write_nbytes(sock_t fd, const char *ptr, int nbytes)
104 {
105    int nleft, nwritten;
106 
107    nleft = nbytes;
108    while (nleft > 0) {
109       nwritten = send(fd, ptr, nleft, 0);
110 
111       switch (nwritten) {
112       case -1:
113          if (errno == EINTR || errno == EAGAIN)
114             continue;
115          return -errno;           /* error */
116       case 0:
117          return nbytes - nleft;   /* EOF */
118       }
119 
120       nleft -= nwritten;
121       ptr += nwritten;
122    }
123 
124    return nbytes - nleft;
125 }
126 
127 /*
128  * Receive a message from the other end. Each message consists of
129  * two packets. The first is a header that contains the size
130  * of the data that follows in the second packet.
131  * Returns number of bytes read
132  * Returns 0 on end of file
133  * Returns -1 on hard end of file (i.e. network connection close)
134  * Returns -2 on error
135  */
net_recv(sock_t sockfd,char * buff,int maxlen)136 int net_recv(sock_t sockfd, char *buff, int maxlen)
137 {
138    int nbytes;
139    unsigned short pktsiz;
140 
141    /* get data size -- in short */
142    if ((nbytes = read_nbytes(sockfd, (char *)&pktsiz, sizeof(pktsiz))) <= 0) {
143       /* probably pipe broken because client died */
144       return nbytes;               /* assume hard EOF received */
145    }
146    if (nbytes != sizeof(pktsiz))
147       return -EINVAL;
148 
149    pktsiz = ntohs(pktsiz);         /* decode no. of bytes that follow */
150    if (pktsiz > maxlen)
151       return -EINVAL;
152    if (pktsiz == 0)
153       return 0;                    /* soft EOF */
154 
155    /* now read the actual data */
156    if ((nbytes = read_nbytes(sockfd, buff, pktsiz)) <= 0)
157       return nbytes;
158    if (nbytes != pktsiz)
159       return -EINVAL;
160 
161    return nbytes;                /* return actual length of message */
162 }
163 
164 /*
165  * Send a message over the network. The send consists of
166  * two network packets. The first is sends a short containing
167  * the length of the data packet which follows.
168  * Returns number of bytes sent, 0 for EOF
169  * Returns -errno on error
170  */
net_send(sock_t sockfd,const char * buff,int len)171 int net_send(sock_t sockfd, const char *buff, int len)
172 {
173    int rc;
174    short pktsiz;
175 
176    /* send short containing size of data packet */
177    pktsiz = htons((short)len);
178    rc = write_nbytes(sockfd, (char *)&pktsiz, sizeof(short));
179    if (rc <= 0)
180       return rc;
181    if (rc != sizeof(short))
182       return -EINVAL;
183 
184    /* send data packet */
185    rc = write_nbytes(sockfd, buff, len);
186    if (rc <= 0)
187       return rc;
188    if (rc != len)
189       return -EINVAL;
190 
191    return rc;
192 }
193 
194 /*
195  * Open a TCP connection to the UPS network server
196  * Returns -errno on error
197  * Returns socket file descriptor otherwise
198  */
net_open(const char * host,char * service,int port)199 sock_t net_open(const char *host, char *service, int port)
200 {
201    int nonblock = 1;
202    int block = 0;
203    sock_t sockfd;
204    int rc;
205    struct sockaddr_in tcp_serv_addr;  /* socket information */
206 
207 #ifndef HAVE_MINGW
208    // Every platform has their own magic way to avoid getting a SIGPIPE
209    // when writing to a stream socket where the remote end has closed.
210    // This method works pretty much everywhere which avoids the mess
211    // of figuring out which incantation this platform supports. (Excepting
212    // for win32 which doesn't support signals at all.)
213    struct sigaction sa;
214    memset(&sa, 0, sizeof(sa));
215    sa.sa_handler = SIG_IGN;
216    sigaction(SIGPIPE, &sa, NULL);
217 #endif
218 
219    /*
220     * Fill in the structure serv_addr with the address of
221     * the server that we want to connect with.
222     */
223    memset((char *)&tcp_serv_addr, 0, sizeof(tcp_serv_addr));
224    tcp_serv_addr.sin_family = AF_INET;
225    tcp_serv_addr.sin_port = htons(port);
226    tcp_serv_addr.sin_addr.s_addr = inet_addr(host);
227    if (tcp_serv_addr.sin_addr.s_addr == INADDR_NONE) {
228       struct hostent he;
229       char *tmphstbuf = NULL;
230       size_t hstbuflen = 0;
231       struct hostent *hp = gethostname_re(host, &he, &tmphstbuf, &hstbuflen);
232       if (!hp)
233       {
234          free(tmphstbuf);
235          Dmsg(100, "%s: gethostname fails: %d\n", __func__, h_errno);
236          return -ENXIO;
237       }
238 
239       if (hp->h_length != sizeof(tcp_serv_addr.sin_addr.s_addr) ||
240           hp->h_addrtype != AF_INET)
241       {
242          free(tmphstbuf);
243          Dmsg(100, "%s: Bad address returned from gethostbyname\n", __func__);
244          return -EAFNOSUPPORT;
245       }
246 
247       memcpy(&tcp_serv_addr.sin_addr.s_addr, hp->h_addr,
248              sizeof(tcp_serv_addr.sin_addr.s_addr));
249       free(tmphstbuf);
250    }
251 
252    /* Open a TCP socket */
253    if ((sockfd = socket_cloexec(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
254    {
255       rc = -errno;
256       Dmsg(100, "%s: socket fails: %s\n", __func__, strerror(-rc));
257       return rc;
258    }
259 
260    /* connect to server */
261 
262    /* Set socket to non-blocking mode */
263    if (ioctl(sockfd, FIONBIO, &nonblock) != 0) {
264       rc = -errno;
265       close(sockfd);
266       Dmsg(100, "%s: ioctl(FIONBIO,nonblock) fails: %s\n", __func__, strerror(-rc));
267       return rc;
268    }
269 
270    /* Initiate connection attempt */
271    rc = connect(sockfd, (struct sockaddr *)&tcp_serv_addr, sizeof(tcp_serv_addr));
272    if (rc == -1 && errno != EINPROGRESS) {
273       rc = -errno;
274       close(sockfd);
275       Dmsg(100, "%s: connect fails: %s\n", __func__, strerror(-rc));
276       return rc;
277    }
278 
279    /* If connection is in progress, wait for it to complete */
280    if (rc == -1) {
281       struct timeval timeout;
282       fd_set fds;
283       int err;
284       socklen_t errlen = sizeof(err);
285 
286       do {
287          /* Expect connection within 5 seconds */
288          timeout.tv_sec = 5;
289          timeout.tv_usec = 0;
290          FD_ZERO(&fds);
291          FD_SET(sockfd, &fds);
292 
293          /* Wait for connection to complete */
294          rc = select(sockfd + 1, NULL, &fds, NULL, &timeout);
295          switch (rc) {
296          case -1: /* select error */
297             if (errno == EINTR || errno == EAGAIN)
298                continue;
299             err = -errno;
300             close(sockfd);
301             Dmsg(100, "%s: select fails: %s\n", __func__, strerror(-err));
302             return err;
303          case 0: /* timeout */
304             close(sockfd);
305             Dmsg(100, "%s: select timeout\n", __func__);
306             return -ETIMEDOUT;
307          }
308       }
309       while (rc == -1 && (errno == EINTR || errno == EAGAIN));
310 
311       /* Connection completed? Check error status. */
312       if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
313          rc = -errno;
314          close(sockfd);
315          Dmsg(100, "%s: getsockopt fails: %s\n", __func__, strerror(-rc));
316          return rc;
317       }
318       if (errlen != sizeof(err)) {
319          close(sockfd);
320          Dmsg(100, "%s: getsockopt bad length\n", __func__);
321          return -EINVAL;
322       }
323       if (err) {
324          close(sockfd);
325          Dmsg(100, "%s: connection completion fails: %s\n", __func__, strerror(err));
326          return -err;
327       }
328    }
329 
330    /* Connection completed successfully. Set socket back to blocking mode. */
331    if (ioctl(sockfd, FIONBIO, &block) != 0) {
332       rc = -errno;
333       close(sockfd);
334       Dmsg(100, "%s: ioctl(FIONBIO,block) fails: %s\n", __func__, strerror(-rc));
335       return rc;
336    }
337 
338    return sockfd;
339 }
340 
341 /* Close the network connection */
net_close(sock_t sockfd)342 void net_close(sock_t sockfd)
343 {
344    close(sockfd);
345 }
346 
347 /*
348  * Accept a TCP connection.
349  * Returns -1 on error.
350  * Returns file descriptor of new connection otherwise.
351  */
net_accept(sock_t fd,struct sockaddr_in * cli_addr)352 sock_t net_accept(sock_t fd, struct sockaddr_in *cli_addr)
353 {
354 #ifdef HAVE_MINGW
355    /* kludge because some idiot defines socklen_t as unsigned */
356    int clilen = sizeof(*cli_addr);
357 #else
358    socklen_t clilen = sizeof(*cli_addr);
359 #endif
360    sock_t newfd;
361 
362    do {
363       newfd = accept_cloexec(fd, (struct sockaddr *)cli_addr, &clilen);
364    } while (newfd == INVALID_SOCKET && (errno == EINTR || errno == EAGAIN));
365 
366    if (newfd < 0)
367       return -errno;                 /* error */
368 
369    return newfd;
370 }
371 #endif                             /* HAVE_NISLIB */
372