d3068e083946f0b8d6d62f2cb40e2e8483b298b0
[ric-plt/lib/rmr.git] / src / rmr / si / src / si95 / siestablish.c
1 // vim: noet sw=4 ts=4:
2 /*
3 ==================================================================================
4     Copyright (c) 2020 Nokia
5     Copyright (c) 2020 AT&T Intellectual Property.
6
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10
11        http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18 ==================================================================================
19 */
20
21
22 /*
23 *-----------------------------------------------------------------------------------
24 *
25 * Mnemonic:             SIestablish
26 * Abstract:i    Prep functions that set up a socket for listening or making a
27 *                               connection.
28 * Date:                 26 March 1995
29 * Author:               E. Scott Daniels
30 *
31 * Modified:     19 Apr 1995 - To keep returned address of the port.
32 *                               08 Mar 2007 - conversion for ipv6.
33 *                               12 Oct 2020 - split into connect prep and listen prep
34 *                                                               functions.
35 *-----------------------------------------------------------------------------------
36 */
37
38
39 #include "sisetup.h"       //  include the necessary setup stuff
40 #include "sitransport.h"
41 #include <errno.h>
42 #include <netinet/tcp.h>
43
44 #ifndef SO_REUSEPORT
45 #define SO_REUSEPORT 0
46 #endif
47
48 /*
49         Prep a socket for "listening."
50         This routine will open a socket and bind an address to it in
51         preparation for listening for connections or inbound UDP
52         datagrams. A file descriptor for the socket is captured and all
53         related information is placed into a transport provider (tp) block.
54
55         Type is the SI constant UDP_DEVICE or TCP_DEVICE
56         abuf points to the address that is to be bound to the socket.
57         Family is one of the AF_* constants (AF_ANY, AF_INET or AF_INET6)
58
59         The address should be one of these forms:
60                         [::1]:port                      // v6 localhost device (loop back)
61                         localhost:port          // v4 or 6 loopback depending on /etc/hosts
62                         0.0.0.0:port            // any interface
63                         addr:port                       // an address assigned to one of the devices
64
65         Returns a transport struct which is the main context for the listener.
66 */
67 extern struct tp_blk *SIlisten_prep( struct ginfo_blk *gptr, int type, char* abuf, int family ) {
68         struct tp_blk *tptr;         //  pointer at new tp block
69         int status = SI_OK;          //  processing status
70         struct sockaddr *addr;          //  IP address we are requesting
71         int protocol;                //  protocol for socket call
72         char buf[256];               //  buffer to build request address in
73         int optval = 0;
74         int alen = 0;
75
76         tptr = (struct tp_blk *) SInew( TP_BLK );     //  new transport info block
77
78         if( tptr != NULL )
79         {
80                 addr = NULL;
81
82                 switch( type )                  //  things specifc to tcp or udp
83                 {
84                         case UDP_DEVICE:
85                                 tptr->type = SOCK_DGRAM;
86                                 protocol = IPPROTO_UDP;
87                                 break;
88
89                         case TCP_DEVICE:
90                         default:
91                                 tptr->type = SOCK_STREAM;
92                                 protocol = IPPROTO_TCP;
93                 }
94
95                 alen = SIgenaddr( abuf, protocol, family, tptr->type, &addr );  //  family == 0 for type that suits the address passed in
96                 if( alen <= 0 ) {
97                         if( addr != NULL ) {
98                                 free( addr );           // not needed, but scanners complain if we don't overtly do this
99                         }
100                         return NULL;
101                 }
102
103                 tptr->family = addr->sa_family;
104
105                 if( (tptr->fd = SOCKET( tptr->family, tptr->type, protocol )) >= SI_OK ) {
106                         optval = 1;
107                         if( SO_REUSEPORT ) {
108                                 SETSOCKOPT(tptr->fd, SOL_SOCKET, SO_REUSEPORT, (char *)&optval, sizeof( optval) ) ;
109                         }
110
111                         status = BIND( tptr->fd, (struct sockaddr *) addr, alen );
112                         if( status == SI_OK ) {
113                                 tptr->addr = addr;                      //  save address
114                         } else {
115                                 fprintf( stderr, "<ERR> siestablish: bind failed: fam=%d type=%d pro=%d %s\n", tptr->family, tptr->type, protocol, strerror( errno ) );
116                                 close( tptr->fd );
117                         }
118                 } else {
119                         status = ! SI_OK;                               //  force bad return later
120                         fprintf( stderr, "<ERR> siestablish: socket not esablished: fam=%d type=%d pro=%d %s\n", tptr->family, tptr->type, protocol, strerror( errno ) );
121                 }
122
123                 if( status != SI_OK ) {                         //  socket or bind call failed - clean up stuff
124                         fprintf( stderr, "<ERR> siestablish: bad state -- returning nil pointer\n" );
125                         free( addr );
126                         SItrash( TP_BLK, tptr );        //  free the trasnsport block
127                         tptr = NULL;                            //  set to return nothing
128                 }
129         }
130
131         return tptr;
132 }
133
134 /*
135         Look at the address and determine if the connect attempt to this address must
136         use safe_connect() rather than the system connect() call. On linux, a smart
137         connect is needed if the target port is >32K and is even. This makes the assumption
138         that the local port rage floor is 32K; we could read something in /proc, but
139         at this point won't bother.  Returns true if we determine that it is best to
140         use safe_connect().
141 */
142 static int need_smartc( char* abuf ) {
143         char*   tok;
144         int             state = 1;
145         int             v;
146
147         if( (tok = strchr( abuf, ':')) != NULL ) {
148                 v = atoi( tok+1 );
149                 if( v < 32767  || v % 2 != 0 ) {
150                         state = 0;
151                 }
152         }
153
154         return state;
155 }
156
157 /*
158         Prep a socket to use to connect to a listener.
159         Establish a transport block and target address in prep to connect.
160         Type is the SI constant UDP_DEVICE or TCP_DEVICE. The abuf pointer
161         should point to either a name:port or IP:port string. Family should
162         be 0 to select the family best suited to the address provided, or
163         any (v4 or v6) if the address is a name. If a perticular type is
164         desired family should be either AF_INET or AF_INET6.  Using a
165         family of 0 (AF_ANY) is usually the best choice.
166 */
167 extern struct tp_blk *SIconn_prep( struct ginfo_blk *gptr, int type, char *abuf, int family ) {
168         struct tp_blk *tptr;         //  pointer at new tp block
169         struct sockaddr *addr;          //  IP address we are requesting
170         int protocol;                //  protocol for socket call
171         char buf[256];               //  buffer to build request address in
172         int optval = 0;
173         int alen = 0;
174
175         tptr = (struct tp_blk *) SInew( TP_BLK );     //  new transport info block
176
177         if( tptr != NULL )
178         {
179                 addr = NULL;
180
181                 switch( type )                  //  things specifc to tcp or udp
182                 {
183                         case UDP_DEVICE:
184                                 tptr->type = SOCK_DGRAM;
185                                 protocol = IPPROTO_UDP;
186                                 break;
187
188                         case TCP_DEVICE:
189                         default:
190                                 tptr->type = SOCK_STREAM;
191                                 protocol = IPPROTO_TCP;
192                 }
193
194                 alen = SIgenaddr( abuf, protocol, family, tptr->type, &addr );  //  family == 0 for type that suits the address passed in
195                 if( alen <= 0 ) {
196                         if( addr != NULL ) {            // not needed, but scanners complain if we don't overtly do this
197                                 free( addr );
198                         }
199                         return NULL;
200                 }
201
202                 tptr->family = addr->sa_family;
203                 tptr->palen = alen;
204
205                 if( (tptr->fd = SOCKET( tptr->family, tptr->type, protocol )) >= SI_OK ) {
206                         optval = 1;
207
208                         if( gptr->tcp_flags & SI_TF_NODELAY ) {
209                                 optval = 1;
210                         } else {
211                                 optval = 0;
212                         }
213                         SETSOCKOPT( tptr->fd, SOL_TCP, TCP_NODELAY, (void *)&optval, sizeof( optval) ) ;
214
215                         if( gptr->tcp_flags & SI_TF_FASTACK ) {
216                                 optval = 1;
217                         } else {
218                                 optval = 0;
219                         }
220                         SETSOCKOPT( tptr->fd, SOL_TCP, TCP_QUICKACK, (void *)&optval, sizeof( optval) ) ;
221
222                         tptr->paddr = addr;                             // tuck the remote peer address away
223                         if( need_smartc( abuf ) ) {
224                                 tptr->flags |= TPF_SAFEC;
225                         }
226                 } else {
227                         free( addr );
228                         SItrash( TP_BLK, tptr );                // free the trasnsport block
229                         tptr = NULL;                                    // we'll return nil
230                 }
231         }
232
233         return tptr;
234 }