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