53c8db54341858fb49191cc3eca20247cc5ff63f
[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                         return NULL;
98                 }
99
100                 tptr->family = addr->sa_family;
101
102                 if( (tptr->fd = SOCKET( tptr->family, tptr->type, protocol )) >= SI_OK ) {
103                         optval = 1;
104                         if( SO_REUSEPORT ) {
105                                 SETSOCKOPT(tptr->fd, SOL_SOCKET, SO_REUSEPORT, (char *)&optval, sizeof( optval) ) ;
106                         }
107
108                         status = BIND( tptr->fd, (struct sockaddr *) addr, alen );
109                         if( status == SI_OK ) {
110                                 tptr->addr = addr;                      //  save address
111                         } else {
112                                 fprintf( stderr, "<ERR> siestablish: bind failed: fam=%d type=%d pro=%d %s\n", tptr->family, tptr->type, protocol, strerror( errno ) );
113                                 close( tptr->fd );
114                         }
115                 } else {
116                         status = ! SI_OK;                               //  force bad return later
117                         fprintf( stderr, "<ERR> siestablish: socket not esablished: fam=%d type=%d pro=%d %s\n", tptr->family, tptr->type, protocol, strerror( errno ) );
118                 }
119
120                 if( status != SI_OK ) {                         //  socket or bind call failed - clean up stuff
121                         fprintf( stderr, "<ERR> siestablish: bad state -- returning nil pointer\n" );
122                         free( addr );
123                         SItrash( TP_BLK, tptr );        //  free the trasnsport block
124                         tptr = NULL;                            //  set to return nothing
125                 }
126         }
127
128         return tptr;
129 }
130
131 /*
132         Look at the address and determine if the connect attempt to this address must
133         use safe_connect() rather than the system connect() call. On linux, a smart
134         connect is needed if the target port is >32K and is even. This makes the assumption
135         that the local port rage floor is 32K; we could read something in /proc, but
136         at this point won't bother.  Returns true if we determine that it is best to
137         use safe_connect().
138 */
139 static int need_smartc( char* abuf ) {
140         char*   tok;
141         int             state = 1;
142         int             v;
143
144         if( (tok = strchr( abuf, ':')) != NULL ) {
145                 v = atoi( tok+1 );
146                 if( v < 32767  || v % 2 != 0 ) {
147                         state = 0;
148                 }
149         }
150
151         return state;
152 }
153
154 /*
155         Prep a socket to use to connect to a listener.
156         Establish a transport block and target address in prep to connect.
157         Type is the SI constant UDP_DEVICE or TCP_DEVICE. The abuf pointer
158         should point to either a name:port or IP:port string. Family should
159         be 0 to select the family best suited to the address provided, or
160         any (v4 or v6) if the address is a name. If a perticular type is
161         desired family should be either AF_INET or AF_INET6.  Using a
162         family of 0 (AF_ANY) is usually the best choice.
163 */
164 extern struct tp_blk *SIconn_prep( struct ginfo_blk *gptr, int type, char *abuf, int family ) {
165         struct tp_blk *tptr;         //  pointer at new tp block
166         struct sockaddr *addr;          //  IP address we are requesting
167         int protocol;                //  protocol for socket call
168         char buf[256];               //  buffer to build request address in
169         int optval = 0;
170         int alen = 0;
171
172         tptr = (struct tp_blk *) SInew( TP_BLK );     //  new transport info block
173
174         if( tptr != NULL )
175         {
176                 addr = NULL;
177
178                 switch( type )                  //  things specifc to tcp or udp
179                 {
180                         case UDP_DEVICE:
181                                 tptr->type = SOCK_DGRAM;
182                                 protocol = IPPROTO_UDP;
183                                 break;
184
185                         case TCP_DEVICE:
186                         default:
187                                 tptr->type = SOCK_STREAM;
188                                 protocol = IPPROTO_TCP;
189                 }
190
191                 alen = SIgenaddr( abuf, protocol, family, tptr->type, &addr );  //  family == 0 for type that suits the address passed in
192                 if( alen <= 0 ) {
193                         return NULL;
194                 }
195
196                 tptr->family = addr->sa_family;
197                 tptr->palen = alen;
198
199                 if( (tptr->fd = SOCKET( tptr->family, tptr->type, protocol )) >= SI_OK ) {
200                         optval = 1;
201
202                         if( gptr->tcp_flags & SI_TF_NODELAY ) {
203                                 optval = 1;
204                         } else {
205                                 optval = 0;
206                         }
207                         SETSOCKOPT( tptr->fd, SOL_TCP, TCP_NODELAY, (void *)&optval, sizeof( optval) ) ;
208
209                         if( gptr->tcp_flags & SI_TF_FASTACK ) {
210                                 optval = 1;
211                         } else {
212                                 optval = 0;
213                         }
214                         SETSOCKOPT( tptr->fd, SOL_TCP, TCP_QUICKACK, (void *)&optval, sizeof( optval) ) ;
215
216                         tptr->paddr = addr;                             // tuck the remote peer address away
217                         if( need_smartc( abuf ) ) {
218                                 tptr->flags |= TPF_SAFEC;
219                         }
220                 } else {
221                         free( addr );
222                         SItrash( TP_BLK, tptr );                // free the trasnsport block
223                         tptr = NULL;                                    // we'll return nil
224                 }
225         }
226
227         return tptr;
228 }