[prev in list] [next in list] [prev in thread] [next in thread] 

List:       freetds
Subject:    Re: [freetds] connect(2) for UDP
From:       "James K. Lowden" <jklowden () freetds ! org>
Date:       2008-12-18 23:40:43
Message-ID: 20081218184043.557bc60b.jklowden () freetds ! org
[Download RAW message or body]

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

["connect_udp.C" (application/octet-stream)]

#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);
	}
}



_______________________________________________
FreeTDS mailing list
FreeTDS@lists.ibiblio.org
http://lists.ibiblio.org/mailman/listinfo/freetds


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic