"Fossies" - the Fresh Open Source Software Archive 
Member "scanssh-2.1/connecter.c" (30 Nov 2004, 18054 Bytes) of package /linux/privat/old/scanssh-2.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "connecter.c" see the
Fossies "Dox" file reference documentation.
1 /*
2 * Copyright 2000-2004 (c) Niels Provos <provos@citi.umich.edu>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/types.h>
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <sys/tree.h>
35 #include <sys/queue.h>
36 #include <sys/socket.h>
37 #include <sys/time.h>
38 #include <netinet/in.h>
39
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <netdb.h>
44 #include <errno.h>
45 #include <string.h>
46 #include <signal.h>
47 #include <fcntl.h>
48 #include <ctype.h>
49 #include <err.h>
50
51 #include <event.h>
52 #include <dnet.h>
53
54 #include "scanssh.h"
55 #include "socks.h"
56
57 #ifdef DEBUG
58 extern int debug;
59 #define DFPRINTF(x) if (debug) fprintf x
60 #else
61 #define DFPRINTF(x)
62 #endif
63
64
65 /* Global imports */
66
67 extern struct queue_list readyqueue;
68 extern int ssh_sendident;
69 extern char *ssh_ipalias;
70
71 extern struct scanner **ss_scanners;
72 extern int ss_nscanners;
73
74 extern struct socksq socks_host;
75
76 extern rand_t *ss_rand;
77
78 /* Local globals */
79 int scan_nhosts; /* Number of hosts that we are scanning */
80
81 #define HTTP_SCAN "HEAD /index.html HTTP/1.0\n\n"
82
83 void ssh_init(struct bufferevent *bev, struct argument *arg);
84 void ssh_finalize(struct bufferevent *bev, struct argument *arg);
85 void ssh_readcb(struct bufferevent *bev, void *parameter);
86 void ssh_writecb(struct bufferevent *bev, void *parameter);
87 void ssh_errorcb(struct bufferevent *bev, short what, void *parameter);
88
89 void socks_init(struct bufferevent *bev, struct argument *arg);
90 void socks_finalize(struct bufferevent *bev, struct argument *arg);
91
92 void socks5_readcb(struct bufferevent *bev, void *parameter);
93 void socks5_writecb(struct bufferevent *bev, void *parameter);
94 void socks5_errorcb(struct bufferevent *bev, short what, void *parameter);
95
96 void socks4_readcb(struct bufferevent *bev, void *parameter);
97 void socks4_writecb(struct bufferevent *bev, void *parameter);
98 void socks4_errorcb(struct bufferevent *bev, short what, void *parameter);
99
100 void http_init(struct bufferevent *bev, struct argument *arg);
101 void http_finalize(struct bufferevent *bev, struct argument *arg);
102 void http_readcb(struct bufferevent *bev, void *parameter);
103 void http_writecb(struct bufferevent *bev, void *parameter);
104 void http_errorcb(struct bufferevent *bev, short what, void *parameter);
105
106 void http_connect_readcb(struct bufferevent *bev, void *parameter);
107 void http_connect_writecb(struct bufferevent *bev, void *parameter);
108
109 void telnet_init(struct bufferevent *bev, struct argument *arg);
110 void telnet_finalize(struct bufferevent *bev, struct argument *arg);
111 void telnet_readcb(struct bufferevent *bev, void *parameter);
112 void telnet_writecb(struct bufferevent *bev, void *parameter);
113 void telnet_errorcb(struct bufferevent *bev, short what, void *parameter);
114
115 struct scanner scanners[] = {
116 {
117 "ssh",
118 "finds versions for SSH, Web and SMTP servers",
119 ssh_init,
120 ssh_finalize,
121 ssh_readcb,
122 ssh_writecb,
123 ssh_errorcb
124 },
125 {
126 "socks5",
127 "detects SOCKS v5 proxy",
128 socks_init,
129 socks_finalize,
130 socks5_readcb,
131 socks5_writecb,
132 socks5_errorcb
133 },
134 {
135 "socks4",
136 "detects SOCKS v4 proxy",
137 socks_init,
138 socks_finalize,
139 socks4_readcb,
140 socks4_writecb,
141 socks4_errorcb
142 },
143 {
144 "http-proxy",
145 "detects HTTP get proxy",
146 http_init,
147 http_finalize,
148 http_readcb,
149 http_writecb,
150 http_errorcb
151 },
152 {
153 "http-connect",
154 "detects HTTP connect proxy",
155 http_init,
156 http_finalize,
157 http_connect_readcb,
158 http_connect_writecb,
159 http_errorcb
160 },
161 {
162 "telnet-proxy",
163 "detects telnet proxy",
164 telnet_init,
165 telnet_finalize,
166 telnet_readcb,
167 telnet_writecb,
168 telnet_errorcb
169 },
170 {
171 NULL, NULL, NULL, NULL
172 }
173 };
174
175 void
176 scanner_print(char *pre)
177 {
178 struct scanner *myscan = &scanners[0];
179
180 while (myscan->name != NULL) {
181 fprintf(stderr, "%s%12s\t%s\n",
182 pre, myscan->name, myscan->description);
183 myscan++;
184 }
185 }
186
187 struct scanner *
188 scanner_find(char *scanner)
189 {
190 struct scanner *myscan = &scanners[0];
191
192 while (myscan->name != NULL) {
193 if (strcmp(myscan->name, scanner) == 0)
194 return (myscan);
195 myscan++;
196 }
197
198 return (NULL);
199 }
200
201 #define SSH_DIDWRITE 0x0001
202 #define SSH_GOTREAD 0x0002
203
204 struct ssh_state {
205 int nlines;
206 char *firstline;
207 };
208
209 void
210 ssh_init(struct bufferevent *bev, struct argument *arg)
211 {
212 if ((arg->a_state = calloc(1, sizeof(struct ssh_state))) == NULL)
213 err(1, "%s: calloc", __func__);
214
215 arg->a_flags = 0;
216 }
217
218 void
219 ssh_finalize(struct bufferevent *bev, struct argument *arg)
220 {
221 struct ssh_state *state = arg->a_state;
222
223 if (state->firstline)
224 free(state->firstline);
225 free(arg->a_state);
226 arg->a_state = NULL;
227 arg->a_flags = 0;
228 }
229
230 int
231 ssh_process_line(struct evbuffer *input, struct argument *arg)
232 {
233 struct ssh_state *state = arg->a_state;
234 while (1) {
235 size_t off = 0;
236 char *p = EVBUFFER_DATA(input);
237
238 while (off < EVBUFFER_LENGTH(input)) {
239 if (*p == '\r') {
240 *p = '\0';
241 } else if (*p == '\n') {
242 *p = '\0';
243 break;
244 }
245 p++;
246 off++;
247 }
248
249 if (off == EVBUFFER_LENGTH(input))
250 return (-1);
251
252 off++;
253 p = EVBUFFER_DATA(input);
254
255 state->nlines++;
256 if (state->firstline == NULL && isprint(*p))
257 state->firstline = strdup(p);
258
259 if (strncmp(p, "SSH-", 4) == 0) {
260 postres(arg, p);
261 return (1);
262 } else if (strncasecmp(p, "Server: ", 8) == 0) {
263 postres(arg, p + 8);
264 return (1);
265 }
266
267 if (state->nlines > 50) {
268 postres(arg, "<error: too many lines>");
269 return (0);
270 }
271
272 evbuffer_drain(input, off);
273 }
274 }
275
276 void
277 ssh_readcb(struct bufferevent *bev, void *parameter)
278 {
279 struct argument *arg = parameter;
280 int res;
281
282 DFPRINTF((stderr, "%s: called\n", __func__));
283
284 if ((res = ssh_process_line(EVBUFFER_INPUT(bev), arg)) == -1)
285 return;
286
287 if (res == 0) {
288 ssh_errorcb(bev, EVBUFFER_READ | EVBUFFER_TIMEOUT, arg);
289 return;
290 }
291
292 arg->a_flags |= SSH_GOTREAD;
293 if (!ssh_sendident || (arg->a_flags & SSH_DIDWRITE))
294 scanhost_return(bev, arg, 1);
295 return;
296 }
297
298 void
299 ssh_writecb(struct bufferevent *bev, void *parameter)
300 {
301 struct argument *arg = parameter;
302
303 DFPRINTF((stderr, "%s: called\n", __func__));
304
305 if (!ssh_sendident || (arg->a_flags & SSH_DIDWRITE)) {
306 if (arg->a_flags & SSH_GOTREAD)
307 scanhost_return(bev, arg, 1);
308 return;
309 }
310
311 if (arg->a_ports[0].port == 80)
312 bufferevent_write(bev, HTTP_SCAN, strlen(HTTP_SCAN));
313 else
314 bufferevent_write(bev, SSHMAPVERSION, sizeof(SSHMAPVERSION));
315 arg->a_flags |= SSH_DIDWRITE;
316 }
317
318 void
319 ssh_errorcb(struct bufferevent *bev, short what, void *parameter)
320 {
321 struct argument *arg = parameter;
322 struct ssh_state *state = arg->a_state;
323 int success = 0;
324
325 if (state->firstline) {
326 postres(arg, state->firstline);
327 success = 1;
328 } else {
329 postres(arg, "<ssh error on %s:%s%s%s: %s>",
330 what & EV_READ ? "read" : "write",
331 what & EVBUFFER_ERROR ? " EV_ERROR" : "",
332 what & EVBUFFER_EOF ? " EV_EOF" : "",
333 what & EVBUFFER_TIMEOUT ? " EV_TIMEOUT" : "",
334 strerror(errno));
335 success = 0;
336 }
337 scanhost_return(bev, arg, success);
338 }
339
340 /* Either connect or bind */
341
342 int
343 make_socket_ai(int (*f)(int, const struct sockaddr *, socklen_t),
344 struct addrinfo *ai)
345 {
346 struct linger linger;
347 int fd, on = 1;
348
349 /* Create listen socket */
350 fd = socket(AF_INET, SOCK_STREAM, 0);
351 if (fd == -1) {
352 warn("socket");
353 return (-1);
354 }
355
356 if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
357 warn("fcntl(O_NONBLOCK)");
358 goto out;
359 }
360
361 if (fcntl(fd, F_SETFD, 1) == -1) {
362 warn("fcntl(F_SETFD)");
363 goto out;
364 }
365
366 setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
367 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on));
368 linger.l_onoff = 1;
369 linger.l_linger = 5;
370 setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
371
372 if ((f)(fd, ai->ai_addr, ai->ai_addrlen) == -1) {
373 if (errno != EINPROGRESS) {
374 warn("%s", __func__);
375 goto out;
376 }
377 }
378
379 return (fd);
380
381 out:
382 close(fd);
383 return (-1);
384 }
385
386 int
387 make_socket(int (*f)(int, const struct sockaddr *, socklen_t),
388 char *address, uint16_t port)
389 {
390 struct addrinfo ai, *aitop;
391 char strport[NI_MAXSERV];
392 int fd;
393
394 memset(&ai, 0, sizeof (ai));
395 ai.ai_family = AF_INET;
396 ai.ai_socktype = SOCK_STREAM;
397 ai.ai_flags = f != connect ? AI_PASSIVE : 0;
398 snprintf(strport, sizeof (strport), "%d", port);
399 if (getaddrinfo(address, strport, &ai, &aitop) != 0) {
400 warn("getaddrinfo");
401 return (-1);
402 }
403
404 fd = make_socket_ai(f, aitop);
405
406 freeaddrinfo(aitop);
407
408 return (fd);
409 }
410
411 int
412 scanhost_check_socketerror(struct argument *arg, short what)
413 {
414 int error;
415 socklen_t errsz = sizeof(error);
416 int fd = arg->a_fd;
417
418 if (what == EV_TIMEOUT) {
419 postres(arg, "<timeout>");
420 goto error;
421 }
422
423 /* Check if the connection completed */
424 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &errsz) == -1) {
425 warn("%s: getsockopt for %d", __func__, fd);
426 postres(arg, "<error: getsockopt>");
427 goto error;
428 }
429
430 if (error) {
431 if (error == ECONNREFUSED) {
432 postres(arg, "<refused>");
433 } else {
434 warnx("%s: getsockopt: %s", __func__, strerror(error));
435 postres(arg, "<errror: getsockopt>");
436 }
437 goto error;
438 }
439
440 return (0);
441
442 error:
443 scanhost_return(NULL, arg, 0);
444 return (-1);
445 }
446
447 struct bufferevent *
448 scanhost_postconnect_setup(struct argument *arg)
449 {
450 struct bufferevent *bev = NULL;
451 int fuzz;
452
453 /* We successfully connected to the host */
454
455 bev = bufferevent_new(arg->a_fd,
456 arg->a_scanner->readcb,
457 arg->a_scanner->writecb,
458 arg->a_scanner->errorcb, arg);
459 if (bev == NULL) {
460 warnx("%s: bufferevent_new", __func__);
461 postres(arg, "<error: memory>");
462 goto error;
463 }
464
465 fuzz = rand_uint16(ss_rand) % 10;
466 bufferevent_settimeout(bev, SHORTWAIT + fuzz, SHORTWAIT + fuzz);
467 bufferevent_enable(bev, EV_READ|EV_WRITE);
468
469 (*arg->a_scanner->init)(bev, arg);
470
471 return (bev);
472
473 error:
474 scanhost_return(NULL, arg, 0);
475 return (NULL);
476 }
477
478 void
479 scanhost_connectcb(int fd, short what, void *parameter)
480 {
481 struct argument *arg = parameter;
482
483 if (scanhost_check_socketerror(arg, what) == -1)
484 return;
485
486 scanhost_postconnect_setup(arg);
487 }
488
489 static void
490 socks_readcb(struct bufferevent *bev, void *parameter)
491 {
492 struct argument *arg = parameter;
493 struct bufferevent *newbev = NULL;
494 struct socks4_cmd reply;
495
496 DFPRINTF((stderr, "%s: called\n", __func__));
497
498 if (EVBUFFER_LENGTH(bev->input) < sizeof(reply))
499 goto error;
500
501 bufferevent_read(bev, &reply, sizeof(reply));
502 DFPRINTF((stderr, "Version: %d, Reply: %d\n",
503 reply.version, reply.command));
504
505 if (0 != reply.version)
506 goto error;
507
508 switch (reply.command) {
509 case SOCKS4_RESP_SUCCESS:
510 break;
511 case SOCKS4_RESP_FAILURE:
512 postres(arg, "<socks error: server failure>");
513 goto done;
514 case SOCKS4_RESP_NOIDENT:
515 postres(arg, "<socks error: no ident>");
516 goto done;
517 case SOCKS4_RESP_BADIDENT:
518 postres(arg, "<socks error: bad ident>");
519 goto done;
520 default:
521 postres(arg, "<socks error: response>");
522 goto done;
523 }
524
525 /* We need to unregister the event's first due to a bug in libevent */
526 bufferevent_disable(bev, EV_READ|EV_WRITE);
527
528 /* Call the original connect callback to take care of the rest */
529 if ((newbev = scanhost_postconnect_setup(arg)) != NULL) {
530 evbuffer_add_buffer(newbev->input, bev->input);
531 /*
532 * If we have more data buffered, the we need to append it
533 * to the new read buffer and if necessary call the read
534 * callback.
535 * Unfortunately, this assumes a lot about the internals of
536 * libevent.
537 */
538 if (EVBUFFER_LENGTH(newbev->input))
539 newbev->readcb(newbev, newbev->cbarg);
540 }
541
542 bufferevent_free(bev);
543 return;
544
545 error:
546 postres(arg, "<socks read error>");
547
548 done:
549 bufferevent_free(bev);
550 scanhost_return(NULL, arg, 0);
551 }
552
553 static void
554 socks_writecb(struct bufferevent *bev, void *parameter)
555 {
556 struct argument *arg = parameter;
557 struct socks4_cmd cmd;
558
559 DFPRINTF((stderr, "%s: called\n", __func__));
560
561 if (arg->a_flags != 0)
562 return;
563
564 /* Connect to the remote server */
565 memset(&cmd, 0, sizeof(cmd));
566 cmd.version = SOCKS_VERSION4;
567 cmd.command = SOCKS_CMD_CONNECT;
568 cmd.dstport = htons(arg->a_ports[0].port);
569 memcpy(&cmd.address.s_addr, &arg->addr.addr_ip,
570 sizeof(struct in_addr));
571
572 bufferevent_write(bev, &cmd, sizeof(cmd));
573 bufferevent_write(bev, SSHUSERAGENT, sizeof(SSHUSERAGENT));
574
575 arg->a_flags = SOCKS_WAITING_COMMANDRESPONSE;
576 bufferevent_enable(bev, EV_READ);
577 }
578
579 void
580 socks_errorcb(struct bufferevent *bev, short what, void *parameter)
581 {
582 struct argument *arg = parameter;
583
584 DFPRINTF((stderr, "%s: called\n", __func__));
585
586 postres(arg, "<socks error on %s:%s%s%s: %s>",
587 what & EV_READ ? "read" : "write",
588 what & EVBUFFER_ERROR ? " EV_ERROR" : "",
589 what & EVBUFFER_EOF ? " EV_EOF" : "",
590 what & EVBUFFER_TIMEOUT ? " EV_TIMEOUT" : "",
591 strerror(errno));
592 bufferevent_free(bev),
593 scanhost_return(NULL, arg, -1);
594 }
595
596 void
597 scanhost_socks_connectcb(int fd, short what, void *parameter)
598 {
599 struct argument *arg = parameter;
600 struct bufferevent *bev = NULL;
601
602 if (scanhost_check_socketerror(arg, what) == -1)
603 return;
604
605 /* We successfully connected to the host */
606
607 bev = bufferevent_new(arg->a_fd, socks_readcb, socks_writecb,
608 socks_errorcb, arg);
609 if (bev == NULL) {
610 warnx("%s: bufferevent_new", __func__);
611 postres(arg, "<error: memory>");
612 goto error;
613 }
614
615 bufferevent_settimeout(bev, 30, 30);
616 bufferevent_disable(bev, EV_READ);
617 bufferevent_enable(bev, EV_WRITE);
618
619 arg->a_flags = 0;
620
621 return;
622
623 error:
624 scanhost_return(NULL, arg, 0);
625 return;
626 }
627
628 int
629 scanhost(struct argument *arg)
630 {
631 struct timeval tv;
632 uint16_t port = arg->a_ports[0].port;
633 void (*cb)(int, short, void *);
634
635 arg->a_flags = 0;
636 if (TAILQ_FIRST(&socks_host) == NULL) {
637 arg->a_fd = make_socket(connect, addr_ntoa(&arg->addr), port);
638 if (arg->a_fd == -1)
639 return (-1);
640
641 cb = scanhost_connectcb;
642 } else {
643 struct socks_host *single_host = TAILQ_FIRST(&socks_host);
644
645 /* Rotate the entries around */
646 TAILQ_REMOVE(&socks_host, single_host, next);
647 TAILQ_INSERT_TAIL(&socks_host, single_host, next);
648
649 arg->a_fd = make_socket(connect,
650 addr_ntoa(&single_host->host), single_host->port);
651 if (arg->a_fd == -1)
652 return (-1);
653 cb = scanhost_socks_connectcb;
654 }
655
656 event_set(&arg->ev, arg->a_fd, EV_WRITE, cb, arg);
657
658 timerclear(&tv);
659 tv.tv_sec = LONGWAIT;
660 event_add(&arg->ev, &tv);
661
662 return (0);
663 }
664
665 /*
666 * Success parameter:
667 * -2 - scanner timeout, stop scanning and go to next host.
668 * -1 - scanner reset?, stop scanning, go to next port.
669 * 0 - current scanner failed, continue with next scanner
670 * 1 - current scanner succeeded, stop scanning and report success
671 */
672
673 void
674 scanhost_return(struct bufferevent *bev, struct argument *arg, int success)
675 {
676 int done = 0;
677
678 if (bev != NULL) {
679 (*arg->a_scanner->finalize)(bev, arg);
680 bufferevent_free(bev);
681 }
682
683 close(arg->a_fd);
684 arg->a_fd = -1;
685 scan_nhosts--;
686
687 /*
688 * If we had success we remove the port, otherwise we attempt to
689 * use a different scanner on it.
690 */
691 arg->a_scanneroff++;
692 if (success == -2) {
693 printres(arg, arg->a_ports[0].port, "<timeout>");
694
695 /* timeout - host is down */
696 while (arg->a_nports)
697 ports_remove(arg, arg->a_ports[0].port);
698
699 } else if (success == -1) {
700 /* reset? */
701 printres(arg, arg->a_ports[0].port, "<refused>");
702 done = 1;
703
704 } else if (bev != NULL && !success &&
705 arg->a_scanneroff < ss_nscanners) {
706 arg->a_scanner = ss_scanners[arg->a_scanneroff];
707 } else {
708 printres(arg, arg->a_ports[0].port, arg->a_res);
709 done = 1;
710 }
711
712 if (done) {
713 arg->a_scanneroff = 0;
714 arg->a_scanner = ss_scanners[0];
715 ports_remove(arg, arg->a_ports[0].port);
716 }
717
718 if (arg->a_nports == 0) {
719 argument_free(arg);
720 if (TAILQ_FIRST(&readyqueue) == NULL &&
721 !probe_haswork() && !scan_nhosts) {
722 struct timeval tv;
723 timerclear(&tv);
724 event_loopexit(&tv);
725 return;
726 }
727 goto done;
728 }
729
730 /*
731 * Insert at the beginning of the list, so that hosts get completed
732 * faster. Otherwise, insertion at the end of a list causes the list
733 * to grow longer and longer without completing hosts.
734 */
735 TAILQ_INSERT_HEAD(&readyqueue, arg, a_next);
736
737 done:
738 /* Cause another host to be contacted */
739 scanhost_fromlist();
740 }
741
742 void
743 scanhost_ready(struct argument *arg)
744 {
745 TAILQ_INSERT_TAIL(&readyqueue, arg, a_next);
746 scanhost_fromlist();
747 }
748
749 void
750 scanhost_fromlist(void)
751 {
752 extern int max_scanqueue_size;
753 struct argument *arg;
754 while (scan_nhosts < max_scanqueue_size &&
755 (arg = TAILQ_FIRST(&readyqueue)) != NULL) {
756
757 /* Out of file descriptors, we need to try again later */
758 if (scanhost(arg) == -1) {
759 TAILQ_REMOVE(&readyqueue, arg, a_next);
760 TAILQ_INSERT_TAIL(&readyqueue, arg, a_next);
761 break;
762 }
763
764 TAILQ_REMOVE(&readyqueue, arg, a_next);
765 scan_nhosts++;
766 }
767 }