Re: connect(2) for UDP

"James K. Lowden" <[email protected]>
Newsgroups gmane.comp.db.tds.freetds
Message-ID <[email protected]>
Peter Deacon wrote:
> 
> UDP has no concept of connection.  Each message stands alone.
> 
> If the server binds to all interfaces and there are multiple interfaces 
> capable of reaching the client the source address of messages from the 
> server should be concidered random/determined by the routing system.

Not only are you right, you're right.  I even read as much after posting
my message.

And I found a server that answers on a different interface!  :-)  And,
using a combination of techniques, I seem to have something that works.  

1.  If I connect only, I miss answers from other interfaces.  
2.  If I bind only, I don't get ECONNREFUSED, only a timeout.  Perhaps
that's me or my implementation. 

Therefore, I do both, using two sockets, of course.  select(2) tells me
which one is ready, and, mirabile dictu, I get what I want: success,
timeout, or  ECONNREFUSED.  


== code ==
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(s1, &fds);
	FD_SET(s2, &fds);

	struct timeval timeout;
	bzero(&timeout, sizeof(timeout));
	timeout.tv_sec = 5;
	timeout.tv_usec = 0;
	
	cout << "reading from " << hostname << ":" << port << " ... " << flush;
	
	if( (erc = select(s2 + 1, &fds, NULL, NULL, &timeout)) == -1 ) {
		perror("select(2) failed");
		return 1;
	}

	if( 0 == erc ) {
		cerr << "timed out, sorry.\n";
		return 1;
	}
	
	cout << erc << " fd ready on " << flush;
	
	int s;
	char buffer[2048];
	
	if( FD_ISSET(s1, &fds) ) {
		s = s1;
		cout << "connected ... " << flush;
	}
	if( FD_ISSET(s2, &fds) ) {
		s = s2;
		cout << "bound ... " << flush;
	}
	
	while( (erc = read(s, &buffer, sizeof(buffer))) == -1 ) {
		perror("could not read");
		return 1;
	}

	cout << "done.  (read " << erc << " bytes)\n" << flush;

== edoc ==

Data arrive on the bound port.  Errors are detected on the connected port:


== output ==
$ ./connect_udp -h db03 -p 1434 
address of db03 is 10.10.10.10
binding local UDP socket ... done
sending  to  db03:1434 ... done.  (sent 1)
connecting UDP socket to db03:1434 ... done
writing  to  db03:1434 ... done.  (wrote 1)
reading from db03:1434 ... 1 fd ready on bound ... done.  (read 411 bytes)

./connect_udp -h localhost -p 1434
address of localhost is 127.0.0.1
binding local UDP socket ... done
sending  to  localhost:1434 ... done.  (sent 1)
connecting UDP socket to localhost:1434 ... done
writing  to  localhost:1434 ... done.  (wrote 1)
reading from localhost:1434 ... 1 fd ready on connected ...
 could not read: Connection refused
== tuptou ==

Too bad none of this is standardized.  Standards?  We don't need no
stinking standards!  

That about wraps up the research.  I think I'll file a bug report and see
if the project maintaner will update FreeTDS.  Oh, wait....

Regards, 

--jkl

_______________________________________________
FreeTDS mailing list
[email protected]
http://lists.ibiblio.org/mailman/listinfo/freetds
connect_udp.C (application/octet-stream, 3.9 KB)
#include <string.h>

#include <sys/socket.h>
#include <netdb.h>
extern int h_errno;
#include <arpa/inet.h>
#include <sys/ioctl.h>

#include <cassert>

#include <algorithm>
#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

enum modes {binding, connecting} mode = binding;

char lower( char c ) {
	return tolower(c);
}

int
main( int argc, char *argv[] )
{
	int s1, s2, ch, port = 0, erc;
	sockaddr_in loc, svr;
	
	string hostname;

	while( (ch = getopt(argc, argv, "c:h:p:")) != -1 ) {
		switch (ch) {
		case 'c': {
				string option(optarg);
				
				transform(option.begin(), option.end(), option.begin(), lower);
				
				if( option == "connect" ) {
					mode = connecting;
				} else { 
					if( option != "bind" ) {
						cerr << "-c {bind|connect}\n";
						return 1;
					}
				}
						
			}
			break;
		case 'h':
			hostname = optarg;
			break;
		case 'p': {
				stringstream s(optarg);
				s >> port;
				assert(port != 0);
			}
			break;
		default:
			exit(1);
		}
	}
	
	if( hostname.empty() ) {
		cerr << "need hostname\n";
		return 1;
	}
	if( port == 0 ) {
		cerr << "need port\n";
		return 1;
	}
	
	hostent *ent = gethostbyname(hostname.c_str());
	
	if( !ent ) {
		herror(hostname.c_str());
		return 1;
	}
	
	memcpy( &svr.sin_addr.s_addr, ent->h_addr_list[0], sizeof(svr.sin_addr.s_addr) );
	
	cout << "address of " << hostname << " is " << inet_ntoa( *reinterpret_cast<in_addr*>(&svr.sin_addr.s_addr) ) << endl;

	svr.sin_family = PF_INET;
	svr.sin_port = htons(port);

	if( (s1 = socket(PF_INET, SOCK_DGRAM, 0)) < 0 ) {
		perror("could not create socket");
		return 1;
	}
	if( (s2 = socket(PF_INET, SOCK_DGRAM, 0)) < 0 ) {
		perror("could not create local socket");
		return 1;
	}


	unsigned long ioctl_nonblocking = 1;
	
	if( (erc = ioctl(s1, FIONBIO, &ioctl_nonblocking)) < 0) {
		perror("could not set nonblocking mode");
		return 1;
	}

	if( (erc = ioctl(s2, FIONBIO, &ioctl_nonblocking)) < 0) {
		perror("could not set nonblocking mode");
		return 1;
	}

	char cmd[3] = { 0x03, '\0' };
	if( 1434 != port ) 
		strcpy(cmd, "A\n");
	
	
	if( mode == binding ) {
		loc.sin_addr.s_addr = INADDR_ANY;
		loc.sin_port = htons((short) 4341);
		loc.sin_family = PF_INET;

		cout << "binding local UDP socket ... " << flush;

		if (bind(s2, (struct sockaddr *) &loc, sizeof(loc)) < 0) {
			perror("failed");
			return 1;
		}
		cout << "done\n";

		cout << "sending  to  " << hostname << ":" << port << " ... " << flush;

		if( (erc = sendto(s2, &cmd, strlen(cmd), 0, (const sockaddr*)&svr, sizeof(svr))) == -1 ) {
			perror("failed");
			return 1;
		}

		cout << "done.  (sent " << erc << ")\n";
	} 
	cout << "connecting UDP socket to " << hostname << ":" << port << " ... " << flush;

	if( (erc = connect(s1, (const sockaddr*)&svr, sizeof(svr))) < 0 ) {
		perror("failed");
		return 1;
	}
	cout << "done\n";

	cout << "writing  to  " << hostname << ":" << port << " ... " << flush;

	if( (erc = write(s1, &cmd, strlen(cmd))) == -1 ) {
		perror("failed");
		return 1;
	}

	cout << "done.  (wrote " << erc << ")\n";
	
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(s1, &fds);
	FD_SET(s2, &fds);

	struct timeval timeout;
	bzero(&timeout, sizeof(timeout));
	timeout.tv_sec = 5;
	timeout.tv_usec = 0;
	
	cout << "reading from " << hostname << ":" << port << " ... " << flush;
	
	if( (erc = select(s2 + 1, &fds, NULL, NULL, &timeout)) == -1 ) {
		perror("select(2) failed");
		return 1;
	}

	if( 0 == erc ) {
		cerr << "timed out, sorry.\n";
		return 1;
	}
	
	cout << erc << " fd ready on " << flush;
	
	int s;
	char buffer[2048];
	
	if( FD_ISSET(s1, &fds) ) {
		s = s1;
		cout << "connected ... " << flush;
	}
	if( FD_ISSET(s2, &fds) ) {
		s = s2;
		cout << "bound ... " << flush;
	}
	
	while( (erc = read(s, &buffer, sizeof(buffer))) == -1 ) {
		perror("could not read");
		return 1;
	}

	cout << "done.  (read " << erc << " bytes)\n" << flush;
	
	for( char *p=buffer; p < buffer + erc; p++ ) {
		if( *p == ';' ) 
			*p = '\n';
		if( isprint(*p) || isspace(*p) )
			putchar(*p);
	}
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.