+// ---------------- internal functions ----------------------------------------------
+
+/*
+ Attempts a connection to addr, and ensures that the linux even port
+ connect bug does not establish the connection back to our process.
+ If we detect this, then we abort the connection and return -1. 0
+ returned on a good connection.
+
+ If we are hit with the even port connection bug we have no choice but
+ to abort the connection which will CLOSE the caller's FD. This may
+ not be expected in some situations, and when we do the errno is set to
+ EBADFD to indicate this. We ensure that this state is returned ONLY
+ if the failure results in a closed fd which the caller will need to reopen.
+*/
+int safe_connect( int fd, struct sockaddr* addr, int alen ) {
+ int state = 0;
+ char caddr[255]; // work buffer for get sock name (v6 addr ~28 bytes, so this is plenty)
+ int calen; // len of the connect generated address
+
+ if( (state = CONNECT( fd, addr, alen )) != 0 ) {
+ if( errno == EBADFD ) { // ensure we return bad fd ONLY if we abort later
+ errno = ECONNABORTED;
+ }
+ return state;
+ }
+
+ if( PARANOID_CHECKS ) {
+ if( alen > sizeof( caddr ) ) { // shouldn't happen, but be safe
+ fprintf( stderr, "safe_connect: address buffer for connect exceeds work space %d > %lu\n", alen, sizeof( caddr ) );
+ errno = E2BIG;
+ return -1;
+ }
+ }
+
+ calen = alen; // we assume a bound address will have the same type, and thus len, as the connect to addr
+ if( getsockname( fd, (struct sockaddr *) &caddr, &calen ) == 0 ) {
+ if( calen != alen || memcmp( addr, &caddr, calen ) != 0 ) { // addresses differ, so it's good
+ errno = 0;
+ return 0;
+ }
+ }
+
+ siabort_conn( fd );
+ errno = EBADFD;
+ return -1;
+}
+