fixes init script for non ipv6 enabled systems #472755
[packages/xinetd.git] / xinetd / connection.c
1 /*
2  * (c) Copyright 1992 by Panagiotis Tsirigotis
3  * (c) Sections Copyright 1998-2001 by Rob Braun
4  * All rights reserved.  The file named COPYRIGHT specifies the terms 
5  * and conditions for redistribution.
6  */
7
8
9 #include "config.h"
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <syslog.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17
18 #include <netinet/tcp.h>
19
20 #include "sio.h"
21 #include "connection.h"
22 #include "sconf.h"
23 #include "msg.h"
24 #include "main.h"
25 #include "state.h"
26 #include "special.h"
27 #include "access.h"
28
29 #define NEW_CONN()            NEW( connection_s )
30 #define FREE_CONN( cop )      FREE( cop )
31
32 /*
33  * Get a new connection request and initialize 'cp' appropriately
34  */
35 static status_e get_connection( struct service *sp, connection_s *cp )
36 {
37    struct service_config *scp = SVC_CONF( sp );
38    socklen_t sin_len;
39    const char *func = "get_connection" ;
40    int on = 1;
41
42    if( SC_IPV4(scp) ) sin_len = sizeof(struct sockaddr_in);
43    if( SC_IPV6(scp) ) sin_len = sizeof(struct sockaddr_in6);
44
45    if ( SVC_SOCKET_TYPE( sp ) == SOCK_STREAM ) {
46       /* If it's a TCP socket, and we're set to wait, the accept is
47        * done by the child process.  Don't set NEW_DESCRIPTOR, since
48        * there isn't one.  The descriptor will be/was removed from
49        * the descriptor set in svc_suspend and re-enabled in svc_resume.
50        */
51       if( SC_WAITS( scp ) ) {
52          cp->co_descriptor = SVC_FD( sp );
53       } else {
54          cp->co_descriptor = accept( SVC_FD( sp ), &(cp->co_remote_address.sa),
55                                      &sin_len ) ;
56          if (cp->co_descriptor != -1)
57              M_SET( cp->co_flags, COF_NEW_DESCRIPTOR ) ;
58       }
59
60       if ( cp->co_descriptor == -1 )
61       {
62          if ((errno == EMFILE) || (errno == ENFILE))
63              cps_service_stop(sp, "no available descriptors");
64          else
65              msg( LOG_ERR, func, "service %s, accept: %m", SVC_ID( sp ) ) ;
66          return( FAILED ) ;
67       }
68
69       if( SC_NODELAY( scp ) && (SC_PROTOVAL( scp ) == IPPROTO_TCP) )
70          if( setsockopt(SVC_FD(sp), IPPROTO_TCP, TCP_NODELAY, 
71                         (char *)&on, sizeof( on ) ) < 0 )
72             msg( LOG_WARNING, func, "service %s, setsockopt: %m", SVC_ID(sp));
73
74       if( SC_KEEPALIVE( scp ) && (SC_PROTOVAL( scp ) == IPPROTO_TCP) )
75       {
76          if( setsockopt(SVC_FD(sp), SOL_SOCKET, SO_KEEPALIVE, 
77                         (char *)&on, sizeof( on ) ) < 0 )
78             msg( LOG_WARNING, func, "service %s, setsockopt: %m", SVC_ID(sp));
79       }
80       
81       if( SC_IPV6(scp) && !(SC_V6ONLY( scp ))  && 
82          (IN6_IS_ADDR_V4MAPPED(&cp->co_remote_address.sa_in6.sin6_addr) || 
83           IN6_IS_ADDR_V4COMPAT(&cp->co_remote_address.sa_in6.sin6_addr)) ) 
84       {
85          int af = AF_INET;
86          if( setsockopt(cp->co_descriptor, IPPROTO_IPV6,
87                IPV6_ADDRFORM, &af, sizeof( af ) ) ) {
88             if( debug.on ) msg( LOG_WARNING, func, "service %s, IPV6_ADDRFORM setsockopt() failed: %m", SVC_ID( sp) );
89          }
90       }
91
92       M_SET( cp->co_flags, COF_HAVE_ADDRESS ) ;
93    }
94    else
95    {
96       if ( SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM )
97       {
98          char t_ch ;
99
100          /*
101           * This trick is done to get the remote address.
102           * select(2) guaranteed that we won't block on the recvfrom
103           */
104          if ( recvfrom( SVC_FD( sp ), &t_ch, 1, MSG_PEEK,
105                               &cp->co_remote_address.sa, &sin_len ) == -1 )
106          {
107             msg( LOG_ERR, func, "service %s, recvfrom: %m", SVC_ID( sp ) ) ;
108             return( FAILED ) ;
109          }
110          M_SET( cp->co_flags, COF_HAVE_ADDRESS ) ;
111       }
112
113       cp->co_descriptor = SVC_FD( sp ) ;
114    }
115
116    return( OK ) ;
117 }
118
119
120
121 /*
122  * Get a connection for the specified service and return a pointer
123  * to a new connection_s
124  */
125 connection_s *conn_new( struct service *sp )
126 {
127    connection_s    new_conn ;
128    connection_s   *cp ;
129    const char     *func = "conn_new" ;
130
131    CLEAR( new_conn ) ;
132
133    /*
134     * The reason we first get the connection and then allocate a
135     * 'connection_s' is because we want to always consume some input.
136     */
137    if ( get_connection( sp, &new_conn ) == FAILED )
138       return( NULL ) ;
139
140    new_conn.co_sp = sp ;
141    SVC_HOLD( sp ) ;
142    
143    if ( SVC_WAITS( sp ) )
144       svc_suspend( sp ) ;
145
146    cp = NEW_CONN() ;
147    if ( cp == CONN_NULL )
148    {
149       out_of_memory( func ) ;
150       conn_free( &new_conn, 0 ) ;
151       CLEAR( new_conn ) ;
152       return( CONN_NULL ) ;
153    }
154    memcpy(cp, &new_conn, sizeof(connection_s));
155    return( cp ) ;
156 }
157
158
159 /*
160  * Release the specified connection.
161  * Certain actions may be performed before doing this:
162  *      - drain of a single UDP packet if the socket type is SOCK_DGRAM
163  */
164 void conn_free( connection_s *cp, int release_mem )
165 {
166    struct service *sp = cp->co_sp ;
167
168    if( cp == NULL )
169       return;
170       if( debug.on )
171          msg( LOG_INFO, "conn_free", "freeing connection") ;
172
173    if( (SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM) && (SVC_IS_ACTIVE( sp )) )
174       drain( cp->co_descriptor ) ;
175
176    if ( SVC_RELE( sp ) == 0 ) {
177       pset_remove( SERVICES( ps ), sp ) ;
178       svc_release( sp );
179    }
180    cp->co_sp = NULL;
181
182    CONN_CLOSE( cp ) ;
183
184    CLEAR( *cp ) ;
185    if (release_mem) {
186       FREE_CONN( cp ) ;
187    }
188 }
189
190 /* This returns a pointer to a local static stack variable.
191  * The behavior is a remnant of inet_ntoa() behavior.
192  */ 
193 const char *conn_addrstr( const connection_s *cp )
194 {
195    static char name[NI_MAXHOST];
196    unsigned int len = 0;
197
198    if( !M_IS_SET( (cp)->co_flags, COF_HAVE_ADDRESS ) )
199       return "<no address>";
200
201    if( cp->co_remote_address.sa.sa_family == AF_INET ) 
202       len = sizeof(struct sockaddr_in);
203    else if( cp->co_remote_address.sa.sa_family == AF_INET6 )
204       len = sizeof(struct sockaddr_in6);
205
206    if( getnameinfo( &cp->co_remote_address.sa, len,
207          name, NI_MAXHOST, NULL, 0, NI_NUMERICHOST ) ) {
208       return "<no address>";
209    }
210    return name;
211 }
212
213 void conn_dump( const connection_s *cp, int fd )
214 {
215    const char *name = conn_addrstr( cp );
216
217    tabprint( fd, 1, "service = %s\n", SVC_ID( cp->co_sp ) ) ;
218    tabprint( fd, 1, "descriptor = %d\n", cp->co_descriptor ) ;
219 #if defined(__GNUC__) && !defined(__arch64__) && !defined(__alpha__)
220    tabprint( fd, 1, "flags = %#llx\n", cp->co_flags ) ;
221 #else
222    tabprint( fd, 1, "flags = %#lx\n", cp->co_flags ) ;
223 #endif
224    tabprint( fd, 1, "remote_address = %s,%d\n", name,
225                               ntohs( cp->co_remote_address.sa_in.sin_port ) ) ;
226 }
227