1 /*-------------------------------------------------------------------------
2  * Copyright (C) 2000 Caldera Systems, Inc
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  *
9  *    Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  *    Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  *    Neither the name of Caldera Systems nor the names of its
17  *    contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA
24  * SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;  LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *-------------------------------------------------------------------------*/
32 
33 /** Outgoing request handler.
34  *
35  * Handles "outgoing" network conversations requests made by other agents
36  * to slpd.(slpd_incoming.c handles reqests made by other agents to slpd)
37  *
38  * @file       slpd_outgoing.c
39  * @author     Matthew Peterson, John Calcote (jcalcote@novell.com)
40  * @attention  Please submit patches to http://www.openslp.org
41  * @ingroup    SlpdCode
42  */
43 
44 #include "slpd_outgoing.h"
45 #include "slpd_property.h"
46 #include "slpd_process.h"
47 #include "slpd_log.h"
48 #include "slpd_knownda.h"
49 
50 #include "slp_message.h"
51 #include "slp_net.h"
52 
53 SLPList G_OutgoingSocketList = { 0, 0, 0 };
54 
55 /** Read a datagram from an outbound socket.
56  *
57  * @param[in] socklist - The list of sockets being monitored.
58  * @param[in] sock - The socket to be read from.
59  */
OutgoingDatagramRead(SLPList * socklist,SLPDSocket * sock)60 void OutgoingDatagramRead(SLPList * socklist, SLPDSocket * sock)
61 {
62    int bytesread;
63    socklen_t peeraddrlen = sizeof(struct sockaddr_storage);
64 
65    (void)socklist;
66 
67    bytesread = recvfrom(sock->fd, (char*)sock->recvbuf->start,
68          G_SlpdProperty.MTU, 0, (struct sockaddr *)&sock->peeraddr,
69          &peeraddrlen);
70    if (bytesread > 0)
71    {
72       sock->recvbuf->end = sock->recvbuf->start + bytesread;
73 
74       if (!sock->sendbuf)
75          /* Some of the error handling code expects a sendbuf to be available
76           * to be emptied, so make sure there is at least a minimal buffer
77           */
78          sock->sendbuf = SLPBufferAlloc(1);
79       SLPDProcessMessage(&sock->peeraddr, &sock->localaddr,
80             sock->recvbuf, &sock->sendbuf, &sock->sendlist);
81 
82       /* Completely ignore the message */
83 
84    }
85 }
86 
87 /** Reconnect an outbound socket.
88  *
89  * @param[in] socklist - The list of sockets being monitored.
90  * @param[in] sock - The socket to be reconnected.
91  */
OutgoingStreamReconnect(SLPList * socklist,SLPDSocket * sock)92 void OutgoingStreamReconnect(SLPList * socklist, SLPDSocket * sock)
93 {
94    char addr_str[INET6_ADDRSTRLEN];
95 
96    (void)socklist;
97 
98    /*-----------------------------------------------------------------*/
99    /* If socket is already being reconnected but is reconnect blocked */
100    /* just return.  Blocking connect sockets will eventually time out */
101    /*-----------------------------------------------------------------*/
102    if (sock->state == STREAM_CONNECT_BLOCK)
103       return;
104 
105 #ifdef DEBUG
106    /* Log that reconnect warning */
107    SLPDLog("WARNING: Reconnect to agent at %s.  "
108            "Agent may not be making efficient \n"
109            "         use of TCP.\n",
110          SLPNetSockAddrStorageToString(&sock->peeraddr,
111                addr_str, sizeof(addr_str)));
112 #endif
113 
114    /*----------------------------------------------------------------*/
115    /* Make sure we have not reconnected too many times               */
116    /* We only allow SLPD_CONFIG_MAX_RECONN reconnection retries      */
117    /* before we stop                                                 */
118    /*----------------------------------------------------------------*/
119    if (sock->reconns == -1)
120       sock->reconns = 0;
121    sock->reconns += 1;
122    if (sock->reconns > SLPD_CONFIG_MAX_RECONN)
123    {
124       SLPDLog("WARNING: Reconnect tries to agent at %s "
125               "exceeded maximum. It\n         is possible that "
126               "the agent is malicious. Check it out!\n",
127             SLPNetSockAddrStorageToString(&sock->peeraddr,
128                   addr_str, sizeof(addr_str)));
129 
130       /*Since we can't connect, remove it as a DA*/
131       SLPDKnownDARemove(&(sock->peeraddr));
132       sock->state = SOCKET_CLOSE;
133       return;
134    }
135 
136    /*----------------------------------------------------------------*/
137    /* Close the existing socket to clean the stream  and open an new */
138    /* socket                                                         */
139    /*----------------------------------------------------------------*/
140    closesocket(sock->fd);
141 
142    if (sock->peeraddr.ss_family == AF_INET)
143       sock->fd = socket(PF_INET, SOCK_STREAM, 0);
144    else if (sock->peeraddr.ss_family == AF_INET6)
145       sock->fd = socket(PF_INET6, SOCK_STREAM, 0);
146 
147    if (sock->fd == SLP_INVALID_SOCKET)
148    {
149       sock->state = SOCKET_CLOSE;
150       return;
151    }
152 
153    /*---------------------------------------------*/
154    /* Set the new socket to enable nonblocking IO */
155    /*---------------------------------------------*/
156 #ifdef _WIN32
157    {
158       u_long fdflags = 1;
159       ioctlsocket(sock->fd, FIONBIO, &fdflags);
160    }
161 #else
162    {
163       int fdflags = fcntl(sock->fd, F_GETFL, 0);
164       fcntl(sock->fd, F_SETFL, fdflags | O_NONBLOCK);
165    }
166 #endif
167 
168    /*--------------------------*/
169    /* Connect a the new socket */
170    /*--------------------------*/
171    if (connect(sock->fd, (struct sockaddr *)&sock->peeraddr,
172          sizeof(struct sockaddr_storage)))
173    {
174 #ifdef _WIN32
175       if (WSAEWOULDBLOCK == WSAGetLastError())
176 #else
177       if (errno == EINPROGRESS)
178 #endif
179       {
180          /* Connect blocked */
181          sock->state = STREAM_CONNECT_BLOCK;
182          return;
183       }
184    }
185 
186    /* Connection occured immediately. Set to WRITE_FIRST so whole */
187    /* packet will be written                                      */
188    sock->state = STREAM_WRITE_FIRST;
189 }
190 
191 /** Read data from an outbound stream-oriented connection.
192  *
193  * @param[in] socklist - The list of sockets being monitored.
194  * @param[in] sock - The socket to be read from.
195  */
OutgoingStreamRead(SLPList * socklist,SLPDSocket * sock)196 void OutgoingStreamRead(SLPList * socklist, SLPDSocket * sock)
197 {
198    int bytesread;
199    char peek[16];
200    socklen_t peeraddrlen = sizeof(struct sockaddr_storage);
201    unsigned int msglen;
202 
203    if (sock->state == STREAM_READ_FIRST)
204    {
205       /*---------------------------------------------------*/
206       /* take a peek at the packet to get size information */
207       /*---------------------------------------------------*/
208       bytesread = recvfrom(sock->fd, peek, 16, MSG_PEEK,
209                         (struct sockaddr *) &(sock->peeraddr), &peeraddrlen);
210       if (bytesread > 0)
211       {
212          /* allocate the recvbuf big enough for the whole message */
213          msglen = PEEK_LENGTH(peek);
214 
215          sock->recvbuf = SLPBufferRealloc(sock->recvbuf, msglen);
216          if (sock->recvbuf)
217             sock->state = STREAM_READ;
218          else
219          {
220             SLPDLog("INTERNAL_ERROR - out of memory!\n");
221             sock->state = SOCKET_CLOSE;
222          }
223       }
224       else if (bytesread == -1)
225       {
226 #ifdef _WIN32
227          if (WSAEWOULDBLOCK != WSAGetLastError())
228 #else
229          if (errno != EWOULDBLOCK)
230 #endif
231          {
232             /* Error occured or connection was closed. Try to reconnect */
233             /* Socket will be closed if connect times out               */
234             OutgoingStreamReconnect(socklist, sock);
235          }
236       }
237       else
238       {
239          /* An EOF occured. This could mean that the other side closed
240           * the connection due to the idle timeout. If we finished some
241           * requests try a reconnect, otherwise simply drop the connection
242           */
243          if (sock->reconns == -1)
244              OutgoingStreamReconnect(socklist,sock);
245          else
246              sock->state = SOCKET_CLOSE;
247       }
248    }
249 
250    if (sock->state == STREAM_READ)
251    {
252       /*------------------------------*/
253       /* recv the rest of the message */
254       /*------------------------------*/
255       bytesread = recv(sock->fd, (char *)sock->recvbuf->curpos,
256             (int)(sock->recvbuf->end - sock->recvbuf->curpos), 0);
257       if (bytesread > 0)
258       {
259          /* reset age because of activity */
260          sock->age = 0;
261 
262          /* move buffer pointers */
263          sock->recvbuf->curpos += bytesread;
264 
265          /* check to see if everything was read */
266          if (sock->recvbuf->curpos == sock->recvbuf->end)
267             switch (SLPDProcessMessage(&(sock->peeraddr), &(sock->localaddr),
268                         sock->recvbuf, &(sock->sendbuf), 0))
269             {
270                case SLP_ERROR_DA_BUSY_NOW:
271                   sock->state = STREAM_WRITE_WAIT;
272                   break;
273                case SLP_ERROR_PARSE_ERROR:
274                case SLP_ERROR_VER_NOT_SUPPORTED:
275                   sock->state = SOCKET_CLOSE;
276                   break;
277                default:
278                   /* End of outgoing message exchange. Unlink   */
279                   /* send buf from to do list and free it       */
280                   SLPBufferFree(sock->sendbuf);
281                   sock->sendbuf = NULL;
282                   sock->state = STREAM_WRITE_FIRST;
283                   /* clear the reconnection count since we actually
284                    * transmitted a successful message exchange. We
285                    * use -1 to indicate that the socket had at
286                    * least one successful communication.
287                    */
288                   sock->reconns = -1;
289                   break;
290             }
291       }
292       else
293       {
294 #ifdef _WIN32
295          if (WSAEWOULDBLOCK != WSAGetLastError())
296 #else
297          if (errno != EWOULDBLOCK)
298 #endif
299          {
300             /* Error occured or connection was closed. Try to reconnect */
301             /* Socket will be closed if connect times out               */
302             OutgoingStreamReconnect(socklist, sock);
303          }
304       }
305    }
306 }
307 
308 /** Check if the socket is still alive, the server may have closed it.
309  *
310  * @param[in] fd - The socket descriptor to check.
311  *
312  * @return zero on timeout; -1 on any type of error (errno is set)
313  *
314  * @note See the UA version of this function in libslp_network.c
315  *
316  * @internal
317  */
NetworkCheckConnection(sockfd_t fd)318 static int NetworkCheckConnection(sockfd_t fd)
319 {
320     int r;
321 #ifdef HAVE_POLL
322     struct pollfd readfd;
323 #else
324     fd_set readfd;
325     struct timeval tv;
326 #endif
327 
328 #ifdef HAVE_POLL
329     readfd.fd = (int)fd;
330     readfd.events = POLLIN;
331     while ((r = poll(&readfd, 1, 0)) == -1 && errno == EINTR)
332         ;
333 #else
334     FD_ZERO(&readfd);
335     FD_SET((int)fd, &readfd);
336     tv.tv_sec = 0;
337     tv.tv_usec = 0;
338     while ((r = select((int)(fd + 1), &readfd, 0, 0, &tv)) == -1 && errno == EINTR)
339         ;
340 #endif
341     /* r == 0 means timeout, everything else is an error */
342     return r == 0 ? 0 : -1;
343 }
344 
345 /** Write data to an outbound, stream-oriented socket.
346  *
347  * @param[in] socklist - The list of sockets being monitored.
348  * @param[in] sock - The socket to be written to.
349  */
OutgoingStreamWrite(SLPList * socklist,SLPDSocket * sock)350 void OutgoingStreamWrite(SLPList * socklist, SLPDSocket * sock)
351 {
352    int byteswritten;
353    int flags = 0;
354 
355 #if defined(MSG_DONTWAIT)
356    flags = MSG_DONTWAIT;
357 #endif
358 
359    if (sock->state == STREAM_WRITE_FIRST)
360    {
361       /* set sendbuf to the first item in the send list if it is not set */
362       if (sock->sendbuf == NULL)
363       {
364          sock->sendbuf = (SLPBuffer) sock->sendlist.head;
365          if (sock->sendbuf == NULL)
366          {
367             /* there is nothing in the to do list */
368             sock->state = STREAM_CONNECT_IDLE;
369             return;
370          }
371          /* Unlink the send buffer we are sending from the send list */
372          SLPListUnlink(&(sock->sendlist), (SLPListItem *) (sock->sendbuf));
373       }
374 
375       /* make sure that the start and curpos pointers are the same */
376       sock->sendbuf->curpos = sock->sendbuf->start;
377       sock->state = STREAM_WRITE;
378 
379       /* test the socket if it was already used */
380       if (sock->reconns == -1 && sock->age > 10)
381       {
382          if (NetworkCheckConnection(sock->fd) != 0)
383          {
384             OutgoingStreamReconnect(socklist,sock);
385             return;
386          }
387       }
388    }
389 
390    if (sock->sendbuf->end - sock->sendbuf->curpos > 0)
391    {
392       byteswritten = send(sock->fd, (char *)sock->sendbuf->curpos,
393             (int)(sock->sendbuf->end - sock->sendbuf->curpos), flags);
394       if (byteswritten > 0)
395       {
396          /* reset age because of activity */
397          sock->age = 0;
398 
399          /* move buffer pointers */
400          sock->sendbuf->curpos += byteswritten;
401 
402          /* check to see if everything was written */
403          if (sock->sendbuf->curpos == sock->sendbuf->end)
404          {
405             /* Message is completely sent. Set state to read the reply */
406             sock->state = STREAM_READ_FIRST;
407          }
408       }
409       else
410       {
411 #ifdef _WIN32
412          if (WSAEWOULDBLOCK != WSAGetLastError())
413 #else
414          if (errno != EWOULDBLOCK)
415 #endif
416          {
417             /* Error occured or connection was closed. Try to reconnect */
418             /* Socket will be closed if connect times out               */
419             OutgoingStreamReconnect(socklist, sock);
420          }
421       }
422    }
423    else
424    {
425       /* nothing to write */
426 #ifdef DEBUG
427       SLPDLog("yikes, an empty socket is being written!\n");
428 #endif
429       sock->state = SOCKET_CLOSE;
430    }
431 }
432 
433 /** Connect for outbound traffic to a specified remote address.
434  *
435  * @param[in] is_TCP - if non-0, this is a tcp connection to the remote device
436  * @param[in] addr - The address to be connected to.
437  *
438  * @remarks Get a pointer to a connected socket that is associated with
439  * the outgoing socket list. If a connected socket already exists on the
440  * outgoing list, a pointer to it is returned, otherwise a new connection
441  * is made and added to the outgoing list
442  *
443  * @return A pointer to socket, or null on error.
444  */
SLPDOutgoingConnect(int is_TCP,struct sockaddr_storage * addr)445 SLPDSocket * SLPDOutgoingConnect(int is_TCP, struct sockaddr_storage * addr)
446 {
447    SLPDSocket * sock = 0;
448 
449    if(is_TCP)
450    {
451       sock = (SLPDSocket *) G_OutgoingSocketList.head;
452       while (sock)
453       {
454         if (sock->state == STREAM_CONNECT_IDLE
455             || sock->state > STREAM_CONNECT_CLOSE)
456         {
457            if (SLPNetCompareAddrs(&(sock->peeraddr), addr) == 0)
458               break;
459         }
460         sock = (SLPDSocket *) sock->listitem.next;
461       }
462 
463       if (sock == 0)
464       {
465          sock = SLPDSocketCreateConnected(addr);
466          if (sock)
467             SLPListLinkTail(&(G_OutgoingSocketList), (SLPListItem *) sock);
468       }
469    }
470    else
471    {
472       sock = SLPDSocketCreateDatagram(addr, DATAGRAM_UNICAST);
473       if (sock)
474       {
475          SLPListLinkTail(&(G_OutgoingSocketList), (SLPListItem *) sock);
476          sock->reconns = 0;
477          sock->age = 0;
478       }
479    }
480 
481    return sock;
482 }
483 
484 /** Check that there is an outgoing socket for the specified address
485  *
486  * @param[in] addr - The address of the peer to check.
487  *
488  * @return a boolean value; true if there is, false if not.
489  */
SLPDHaveOutgoingConnectedSocket(struct sockaddr_storage * addr)490 int SLPDHaveOutgoingConnectedSocket(struct sockaddr_storage* addr)
491 {
492    SLPDSocket* sock = (SLPDSocket*)G_OutgoingSocketList.head;
493    while (sock)
494    {
495       if (sock->state >= STREAM_CONNECT_IDLE &&
496             SLPNetCompareAddrs(&sock->peeraddr, addr) == 0)
497          return 1;
498       sock = (SLPDSocket*)sock->listitem.next;
499    }
500    return 0;
501 }
502 
503 /** Writes the datagram to the socket's peeraddr
504  *
505  * @param[in] sock - The socket whose peer will be sent to.
506  * @param[in] buffer - The buffer to send, could be the sockets sendbuf, or an item in the sendlist, etc.
507  */
SLPDOutgoingDatagramWrite(SLPDSocket * sock,SLPBuffer buffer)508 void SLPDOutgoingDatagramWrite(SLPDSocket * sock, SLPBuffer buffer)
509 {
510    if (0 >= sendto(sock->fd, (char*)buffer->start,
511                (int)(buffer->end - buffer->start), 0,
512                (struct sockaddr *)&sock->peeraddr,
513                SLPNetAddrLen(&sock->peeraddr)))
514    {
515 #ifdef DEBUG
516       SLPDLog("ERROR: Data could not send() in SLPDOutgoingDatagramWrite()\n");
517 #endif
518    }
519 }
520 
521 /** Writes the datagram to the mcastaddr
522  *
523  * @param[in] sock - The socket to send on
524  * @param[in] maddr - The mcast addr to send to
525  * @param[in] buffer - the buffer to send, could be the sockets sendbuf, or an item in the sendlist, etc.
526  */
SLPDOutgoingDatagramMcastWrite(SLPDSocket * sock,struct sockaddr_storage * maddr,SLPBuffer buffer)527 void SLPDOutgoingDatagramMcastWrite(SLPDSocket * sock, struct sockaddr_storage *maddr, SLPBuffer buffer)
528 {
529    if (sendto(sock->fd, (char*)buffer->start,
530                (int)(buffer->end - buffer->start), 0,
531                (struct sockaddr *)maddr, SLPNetAddrLen(maddr)) < 0)
532    {
533 #ifdef DEBUG
534       SLPDLog("ERROR: Data could not send() in SLPDOutgoingDatagramMcastWrite()\n");
535 #endif
536    }
537 }
538 
539 /** Handles outgoing requests pending on specified file discriptors.
540  *
541  * @param[in,out] fdcount - The number of file descriptors marked in fd_sets.
542  * @param[in] fdset - The set of file descriptors with pending read/write IO.
543  */
SLPDOutgoingHandler(int * fdcount,SLPD_fdset * fdset)544 void SLPDOutgoingHandler(int * fdcount, SLPD_fdset * fdset)
545 {
546    SLPDSocket * sock;
547    sock = (SLPDSocket *) G_OutgoingSocketList.head;
548    while (sock && *fdcount)
549    {
550       if (SLPD_fdset_readok(fdset, sock))
551       {
552          switch (sock->state)
553          {
554             case DATAGRAM_MULTICAST:
555             case DATAGRAM_BROADCAST:
556             case DATAGRAM_UNICAST:
557                OutgoingDatagramRead(&G_OutgoingSocketList, sock);
558                break;
559 
560             case STREAM_READ:
561             case STREAM_READ_FIRST:
562                OutgoingStreamRead(&G_OutgoingSocketList, sock);
563                break;
564 
565             default:
566                /* No SOCKET_LISTEN sockets should exist */
567                break;
568          }
569 
570          *fdcount = *fdcount - 1;
571       }
572       else if (SLPD_fdset_writeok(fdset, sock))
573       {
574          switch (sock->state)
575          {
576             case STREAM_CONNECT_BLOCK:
577                sock->age = 0;
578                sock->state = STREAM_WRITE_FIRST;
579 
580             case STREAM_WRITE:
581             case STREAM_WRITE_FIRST:
582                OutgoingStreamWrite(&G_OutgoingSocketList, sock);
583                break;
584 
585             default:
586                break;
587          }
588 
589          *fdcount = *fdcount - 1;
590       }
591 
592       sock = (SLPDSocket *) sock->listitem.next;
593    }
594 }
595 
596 /** Resend messages on sockets whose timeout has expired
597  *
598  * @param[in] seconds - The number of seconds old a socket must be to have
599  * its messages resent.
600  *
601  * @remarks - Ideally, this would be at a resolution lower than one second,
602  * but given the default timeout values, this isn't too far off the mark, and
603  * should not add too much of a burden to the main loop.
604  */
SLPDOutgoingRetry(time_t seconds)605 void SLPDOutgoingRetry(time_t seconds)
606 {
607    SLPDSocket * del = 0;
608    SLPDSocket * sock = (SLPDSocket *) G_OutgoingSocketList.head;
609 
610    if(seconds <= 0)
611       return;
612 
613    while (sock)
614    {
615       switch (sock->state)
616       {
617        case DATAGRAM_UNICAST:
618           if(0 == sock->sendlist.count)  /*Clean up as fast as we can, as all messages were sent*/
619              del = sock;
620           else
621           {
622              sock->age += seconds;
623              if(sock->age >= G_SlpdProperty.unicastTimeouts[sock->reconns] / 1000)
624              {
625                ++sock->reconns;
626                if(sock->reconns >= MAX_RETRANSMITS)
627                {
628                   char addr_str[INET6_ADDRSTRLEN];
629                   SLPDLog("SLPD: Didn't receive response from DA at %s, removing it from list.\n",
630                   SLPNetSockAddrStorageToString(&sock->peeraddr, addr_str, sizeof(addr_str)));
631 
632                   SLPDKnownDARemove(&(sock->peeraddr));
633                   del = sock;
634                }
635                else
636                {
637                   SLPBuffer pbuf;
638                   sock->age = 0;
639                   for(pbuf = (SLPBuffer) sock->sendlist.head; pbuf; pbuf = (SLPBuffer) pbuf->listitem.next)
640                      SLPDOutgoingDatagramWrite(sock, pbuf);
641                }
642              }
643           }
644             break;
645 
646          case DATAGRAM_MULTICAST:
647          case DATAGRAM_BROADCAST:
648          case STREAM_READ_FIRST:
649          case STREAM_WRITE_FIRST:
650          case STREAM_CONNECT_BLOCK:
651          case STREAM_READ:
652          case STREAM_WRITE:
653          case STREAM_CONNECT_IDLE:
654          case STREAM_WRITE_WAIT:
655          default:
656             break;
657       }
658 
659       sock = (SLPDSocket *) sock->listitem.next;
660 
661       if (del)
662       {
663          SLPDSocketFree((SLPDSocket *)
664                SLPListUnlink(&G_OutgoingSocketList, (SLPListItem *) del));
665          del = 0;
666       }
667    }
668 }
669 
670 
671 
672 /** Age the outgoing socket list.
673  *
674  * @param[in] seconds - The number of seconds old an entry must be to be
675  *    removed from the outgoing list.
676  */
SLPDOutgoingAge(time_t seconds)677 void SLPDOutgoingAge(time_t seconds)
678 {
679    SLPDSocket * del = 0;
680    SLPDSocket * sock = (SLPDSocket *) G_OutgoingSocketList.head;
681 
682    while (sock)
683    {
684       switch (sock->state)
685       {
686          case DATAGRAM_MULTICAST:
687          case DATAGRAM_BROADCAST:
688             if (sock->age > G_SlpdProperty.unicastMaximumWait / 1000)
689                del = sock;
690             sock->age = sock->age + seconds;
691             break;
692 
693        case DATAGRAM_UNICAST:
694           /*The Retry logic ages these out*/
695           break;
696 
697          case STREAM_READ_FIRST:
698          case STREAM_WRITE_FIRST:
699             sock->age = 0;
700             break;
701 
702          case STREAM_CONNECT_BLOCK:
703          case STREAM_READ:
704          case STREAM_WRITE:
705             if (G_OutgoingSocketList.count > SLPD_COMFORT_SOCKETS)
706             {
707                /* Accelerate ageing cause we are low on sockets */
708                if (sock->age > SLPD_CONFIG_BUSY_CLOSE_CONN)
709                {
710                   /* Remove peer from KnownDAs since it might be dead */
711                   SLPDKnownDARemove(&(sock->peeraddr));
712                   del = sock;
713                }
714             }
715             else
716             {
717                if (sock->age > SLPD_CONFIG_CLOSE_CONN)
718                {
719                   /* Remove peer from KnownDAs since it might be dead */
720                   SLPDKnownDARemove(&(sock->peeraddr));
721                   del = sock;
722                }
723             }
724             sock->age = sock->age + seconds;
725             break;
726 
727          case STREAM_CONNECT_IDLE:
728             if (G_OutgoingSocketList.count > SLPD_COMFORT_SOCKETS)
729             {
730                /* Accelerate ageing cause we are low on sockets */
731                if (sock->age > SLPD_CONFIG_BUSY_CLOSE_CONN)
732                   del = sock;
733             }
734             else
735             {
736                if (sock->age > SLPD_CONFIG_CLOSE_CONN)
737                   del = sock;
738             }
739             sock->age = sock->age + seconds;
740             break;
741 
742          case STREAM_WRITE_WAIT:
743             /* this when we are talking to a busy DA */
744             sock->age = 0;
745             sock->state = STREAM_WRITE_FIRST;
746             break;
747 
748          default:
749             /* don't age the other sockets at all */
750             break;
751       }
752 
753       sock = (SLPDSocket *) sock->listitem.next;
754 
755       if (del)
756       {
757          SLPDSocketFree((SLPDSocket *)
758                SLPListUnlink(&G_OutgoingSocketList, (SLPListItem *) del));
759          del = 0;
760       }
761    }
762 }
763 
764 /** Initialize outgoing socket list for all network interfaces.
765  *
766  * @return Zero - always.
767  */
SLPDOutgoingInit(void)768 int SLPDOutgoingInit(void)
769 {
770    /*------------------------------------------------------------*/
771    /* First, remove all of the sockets that might be in the list */
772    /*------------------------------------------------------------*/
773    while (G_OutgoingSocketList.count)
774       SLPDSocketFree((SLPDSocket *)
775             SLPListUnlink(&G_OutgoingSocketList,
776                   (SLPListItem *) G_OutgoingSocketList.head));
777 
778    return 0;
779 }
780 
781 /** Release all unused socket on inbound socket list.
782  *
783  * Deinitialize incoming socket list to have appropriate sockets for all
784  * network interfaces.
785  *
786  * @param[in] graceful - Flag indicates do NOT close sockets with pending
787  *    writes outstanding.
788  *
789  * @return Zero on success, or a non-zero value when pending writes
790  *    remain.
791  */
SLPDOutgoingDeinit(int graceful)792 int SLPDOutgoingDeinit(int graceful)
793 {
794    SLPDSocket * del = 0;
795    SLPDSocket * sock = (SLPDSocket *) G_OutgoingSocketList.head;
796 
797    while (sock)
798    {
799       /* graceful only closes sockets without pending I/O */
800       if (graceful == 0)
801          del = sock;
802       else if (sock->state < SOCKET_PENDING_IO)
803          del = sock;
804 
805       sock = (SLPDSocket *) sock->listitem.next;
806 
807       if (del)
808       {
809          SLPDSocketFree((SLPDSocket *)
810                SLPListUnlink(&G_OutgoingSocketList, (SLPListItem *) del));
811          del = 0;
812       }
813    }
814 
815    return G_OutgoingSocketList.count;
816 }
817 
818 #ifdef DEBUG
819 /** Dump outbound socket data.
820  *
821  * @note This routine is compiled in Debug code only.
822  */
SLPDOutgoingSocketDump(void)823 void SLPDOutgoingSocketDump(void)
824 {
825    char str1[INET6_ADDRSTRLEN];
826    char str2[INET6_ADDRSTRLEN];
827    char str3[INET6_ADDRSTRLEN];
828    SLPDSocket * sock = (SLPDSocket *) G_OutgoingSocketList.head;
829    SLPDLog("========================================================================\n");
830    SLPDLog("Dumping OutgoingSocketList\n");
831    SLPDLog("========================================================================\n");
832    while (sock)
833    {
834       SLPDLog("localaddr=%s peeraddr=%s mcastaddr=%s\n",
835          SLPNetSockAddrStorageToString(&(sock->localaddr), str1, sizeof(str1)),
836          SLPNetSockAddrStorageToString(&(sock->peeraddr), str2, sizeof(str2)),
837          SLPNetSockAddrStorageToString(&(sock->mcastaddr), str3, sizeof(str3)));
838       sock = (SLPDSocket *) sock->listitem.next;
839    }
840 }
841 #endif
842 
843 /*=========================================================================*/
844