5649fabcb2fc7e00da1184485dcacc5e4451eb07
[ric-plt/lib/rmr.git] / src / rmr / si / src / si95 / siconnect.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 *  Mnemonic:    SIconnect
25 *  Abstract:    This module contains functions to make the connection using
26 *                               a transport block which has been given a transport (tcp) family
27 *                               address structure.
28 *
29 *  Date:                March 1995
30 *  Author:              E. Scott Daniels
31 *
32 *  Mod:                 08 Mar 2007 - conversion of sorts to support ipv6
33 *                               17 Apr 2020 - Add safe connect capabilities
34 ******************************************************************************
35 */
36 #include <netinet/tcp.h>
37
38 #include "sisetup.h"
39 #include "sitransport.h"
40
41 // ---------------- internal functions ----------------------------------------------
42
43 /*
44         Attempts a connection to addr, and ensures that the linux even port
45         connect bug does not establish the connection back to our process.
46         If we detect this, then we abort the connection and return -1. 0
47         returned on a good connection.
48
49         If we are hit with the even port connection bug we have no choice but
50         to abort the connection which will CLOSE the caller's FD. This may
51         not be expected in some situations, and when we do the errno is set to
52         EBADFD to indicate this. We ensure that this state is returned ONLY
53         if the failure results in a closed fd which the caller will need to reopen.
54 */
55 int safe_connect( int fd, struct sockaddr* addr, int alen ) {
56         int state = 0;
57         char    caddr[255];                     // work buffer for get sock name (v6 addr ~28 bytes, so this is plenty)
58         int             calen;                          // len of the connect generated address
59
60         if( (state = CONNECT( fd, addr, alen )) != 0 ) {
61                 if( errno == EBADFD ) {                 // ensure we return bad fd ONLY if we abort later
62                         errno = ECONNABORTED;
63                 }
64                 return state;
65         }
66
67         if( PARANOID_CHECKS ) {
68                 if( alen > sizeof( caddr ) ) {          // shouldn't happen, but be safe
69                         fprintf( stderr, "safe_connect: address buffer for connect exceeds work space %d > %lu\n", alen, sizeof( caddr ) );
70                         errno = E2BIG;
71                         return -1;
72                 }
73         }
74
75         calen = alen;                   // we assume a bound address will have the same type, and thus len, as the connect to addr
76         if( getsockname( fd, (struct sockaddr *) &caddr, &calen ) == 0 ) {
77                 if( calen != alen || memcmp( addr, &caddr, calen ) != 0 ) {                     // addresses differ, so it's good
78                         errno = 0;
79                         return 0;
80                 }
81         }
82
83         siabort_conn( fd );
84         errno = EBADFD;
85         return -1;
86 }
87
88 /*
89         Accept a file descriptor and add it to the map.
90 */
91 extern void SImap_fd( struct ginfo_blk *gptr, int fd, struct tp_blk* tpptr ) {
92         if( fd < MAX_FDS ) {
93                 gptr->tp_map[fd] = tpptr;
94         } else {
95                 rmr_vlog( RMR_VL_WARN, "fd on connected session is out of range: %d\n", fd );
96         }
97 }
98
99 /*
100         Creates a connection to the target endpoint using the address in the
101         buffer provided.  The address may be one of three forms:
102                 hostname:port
103                 IPv4-address:port
104                 [IPv6-address]:port
105
106         On success the open file descriptor is returned; else -1 is returned. Errno
107         will be left set by the underlying connect() call.
108
109         To avoid the even port connect bug in the linux connect() systeem call,
110         we will use safe_connect() if indicated during the connection prep
111         process.
112 */
113 extern int SIconnect( struct ginfo_blk *gptr, char *abuf ) {
114         int status;
115         struct tp_blk *tpptr;           //  pointer to new block
116         struct sockaddr *taddr;         // convenience pointer to addr of target
117         int alen = 0;                                   //  len of address struct
118         int fd = SI_ERROR;              //  file descriptor to return to caller
119         int optvalrlen;
120         int optvalr;
121         int optvalw;
122         if( PARANOID_CHECKS ) {
123                 if( gptr == NULL ) {
124                         return SI_ERROR;
125                 }
126
127                 if( gptr->magicnum != MAGICNUM ) {              // no cookie -- no connection
128                         return SI_ERROR;
129                 }
130         }
131
132         tpptr = SIconn_prep( gptr, TCP_DEVICE, abuf, 0 );                       // create tp struct, and socket. get peer address 0 == any family that suits the addr
133         if( tpptr != NULL ) {
134                 taddr = tpptr->paddr;
135                 errno = 0;
136
137                 if( gptr->tcp_flags & SI_TF_QUICK ) {
138                         optvalrlen = sizeof(optvalr);
139                         GETSOCKOPT( tpptr->fd, IPPROTO_TCP, TCP_SYNCNT, (void *)&optvalr, &optvalrlen) ;
140                         optvalw=2;
141                         SETSOCKOPT( tpptr->fd, IPPROTO_TCP, TCP_SYNCNT, (void *)&optvalw, sizeof( optvalw) ) ;
142                 }
143
144                 if( tpptr->flags & TPF_SAFEC ) {
145                         if( safe_connect( tpptr->fd, taddr, tpptr->palen ) != 0 ) {             // fd closed on failure
146                                 tpptr->fd = -1;
147                         }
148                 } else {
149                         if( CONNECT( tpptr->fd, taddr, tpptr->palen ) != 0 ) {
150                                 CLOSE( tpptr->fd );                             // clean up fd and tp_block
151                                 tpptr->fd = -1;
152                         }
153                 }
154
155                 if( tpptr->fd >= 0 ) {                                                          // connect ok
156                         if( gptr->tcp_flags & SI_TF_QUICK ) {
157                                 SETSOCKOPT( tpptr->fd, IPPROTO_TCP, TCP_SYNCNT, (void *)&optvalr, sizeof( optvalr) ) ;
158                         }
159
160                         tpptr->flags |= TPF_SESSION;                    //  indicate we have a session here
161                         tpptr->next = gptr->tplist;                     //  add block to the list
162                         if( tpptr->next != NULL ) {
163                                 tpptr->next->prev = tpptr;              //  if there - point back at new
164                         }
165
166                         gptr->tplist = tpptr;                           //  point at new head
167                         fd = tpptr->fd;                                 //  save for return value
168                         SImap_fd( gptr, fd, tpptr );
169                 } else {
170                         SItrash( TP_BLK, tpptr );               // free the trasnsport block
171                 }
172         }
173
174         return fd;
175 }