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