More socket work
[ITFoundation.git] / ITInetSocket.m
1 //
2 //  ITInetSocket.m
3 //  ITFoundation
4 //
5 //  Created by Alexander Strange on Tue Feb 11 2003.
6 //  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
7 //
8
9 #import "ITInetSocket.h"
10 #import "ITServiceBrowserDelegate.h"
11 #import <sys/socket.h>
12 #import <arpa/inet.h>
13
14
15 @interface ITInetSocket(Debugging)
16 -(NSString*)dumpv6Addrinfo:(struct addrinfo *)_ai;
17 @end
18
19 @interface ITInetSocket(Private)
20 -(void)doConnSetupWithHost:(NSString*)host namedPort:(NSString*)namedPort;
21 @end
22
23 @implementation ITInetSocket
24 +(void)startAutoconnectingToService:(NSString*)type delegate:(id)d
25 {
26     NSNetServiceBrowser *browse = [[NSNetServiceBrowser alloc] init];
27     ITServiceBrowserDelegate *bd = [[ITServiceBrowserDelegate alloc] initWithDelegate:d];
28
29     [browse setDelegate:bd];
30     [browse searchForServicesOfType:[NSString stringWithFormat:@"._%@._tcp",type] inDomain:nil];
31 }
32
33 -(id)initWithFD:(int)fd delegate:(id)d
34 {
35     if (self = [super init])
36            {
37            state = ITInetSocketListening;
38            sockfd = fd;
39            delegate = [d retain];
40            port = 0;
41            writePipe = [[ITByteStream alloc] init];
42            readPipe = [[ITByteStream alloc] init];
43            ai = nil;
44            sarr = nil;
45            }
46     return self;
47 }
48
49 -(void) dealloc
50 {
51     shutdown(sockfd,2);
52     [delegate release];
53     [writePipe release];
54     [readPipe release];
55     if (!sarr) freeaddrinfo(ai);
56     [sarr release];
57 }
58
59 -(id)initWithDelegate:(id)d
60 {
61     if (self = [super init])
62            {
63            state = ITInetSocketDisconnected;
64            sockfd = -1;
65            delegate = [d retain];
66            port = 0;
67            writePipe = [[ITByteStream alloc] init];
68            readPipe = [[ITByteStream alloc] init];
69            ai = nil;
70            sarr = nil;
71            }
72     return self;
73 }
74
75 -(void) connectToHost:(NSString*)host onPort:(short)thePort
76 {
77     if (state == ITInetSocketDisconnected)
78            {
79            NSString *nport = [[NSNumber numberWithShort:thePort] stringValue];
80            [self doConnSetupWithHost:host namedPort:nport];
81            }
82 }
83
84 -(void) connectToHost:(NSString*)host onNamedPort:(NSString*)_port
85 {
86     if (state == ITInetSocketDisconnected)
87            {
88            [self doConnSetupWithHost:host namedPort:_port];
89            }
90 }
91
92 -(void) connectWithSockaddrArray:(NSArray*)arr
93 {
94     NSEnumerator *e = [arr objectEnumerator];
95     NSData *d;
96     struct addrinfo *a;
97     ai = malloc(sizeof(struct addrinfo));
98     a = ai;
99     while (d = [e nextObject])
100            {
101            struct sockaddr *s = (struct sockaddr*)[d bytes];
102            a->ai_family = s->sa_family;
103            a->ai_addr = s;
104            a->ai_next = malloc(sizeof(struct addrinfo));
105            a = a->ai_next;
106            }
107 }
108
109 -(ITInetSocketState)state
110 {
111     return state;
112 }
113 @end
114
115 @implementation ITInetSocket(Debugging)
116 -(NSString*)dumpv6Addrinfo:(struct addrinfo *)_ai
117 {
118     const char *cfmt =
119     "\{\n"
120     "Flags = %x\n"
121     "Family = %x\n"
122     "Socktype = %x\n"
123     "Protocol = %x\n"
124     "Canonname = %s\n"
125     "Sockaddr = \n"
126     "{\n"
127     "\tLength = %x\n"
128     "\tFamily = %x\n"
129     "\tPort = %d\n"
130     "\tFlowinfo = %x\n"
131     "\tAddr = {%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx}\n"
132     "\tScope = %x\n"
133     "}\n"
134     "Next = ";
135     NSString *nsfmt = [NSString stringWithCString:cfmt];
136     NSMutableString *buf = [[[NSMutableString alloc] init] autorelease];
137
138     do
139            {
140                   struct sockaddr_in6 *sa = (struct sockaddr_in6 *)_ai->ai_addr;
141                   [buf appendFormat:nsfmt,_ai->ai_flags,_ai->ai_family,_ai->ai_socktype,_ai->ai_protocol,_ai->ai_canonname?_ai->ai_canonname:"",sa->sin6_len,sa->sin6_family,sa->sin6_port,sa->sin6_flowinfo,sa->sin6_addr.__u6_addr.__u6_addr16[0],sa->sin6_addr.__u6_addr.__u6_addr16[1],sa->sin6_addr.__u6_addr.__u6_addr16[2],sa->sin6_addr.__u6_addr.__u6_addr16[3],sa->sin6_addr.__u6_addr.__u6_addr16[4],sa->sin6_addr.__u6_addr.__u6_addr16[5],sa->sin6_addr.__u6_addr.__u6_addr16[6],sa->sin6_addr.__u6_addr.__u6_addr16[7],sa->sin6_scope_id];
142            }
143     while (_ai = _ai->ai_next);
144     [buf appendString:@"nil\n}"];
145     return buf;
146 }
147 @end
148
149 @implementation ITInetSocket(Private)
150 -(void)doConnSetupWithHost:(NSString*)host namedPort:(NSString*)namedPort
151 {
152     struct addrinfo hints;
153            int err;
154            const char *portNam = [namedPort cString], *hostCStr = [host cString];
155
156            hints.ai_flags = 0;
157            hints.ai_family = PF_INET6;
158            hints.ai_socktype = SOCK_STREAM;
159            hints.ai_protocol = IPPROTO_TCP;
160            hints.ai_canonname = NULL;
161            hints.ai_addr = NULL;
162            hints.ai_next = NULL;
163
164            err = getaddrinfo(hostCStr,portNam,&hints,&ai);
165            if (err == EAI_NODATA) //it's a dotted-decimal IPv4 string, so we use v6compat stuff now
166            {
167                   err = getaddrinfo([[NSString stringWithFormat:@"ffff::%s",hostCStr] cString],portNam,&hints,&ai);
168            }
169            NSLog([self dumpv6Addrinfo:ai]);
170 }
171 @end