This is MiscINETSocket.m in view mode; [Download] [Up]
/* Class for handling sockets in the Internet domain.
*
* Copyright (c) 1996 Aleksey Sudakov <zander@cnext.crec.mipt.ru>.
*
* This software is subject to the terms of the MiscKit license
* agreement. Refer to the license document included with the
* MiscKit distribution for these terms.
*
* Version 1.0 BETA (16 December 1995)
*/
/*
#import <misckit/MiscINETSocket.h>
*/
#import "MiscINETSocket.h"
#import <Foundation/NSData.h>
#import <Foundation/NSCoder.h>
#import <objc/HashTable.h>
#ifdef WIN32
#import <stdlib.h>
#import <stdio.h>
#import <winsock.h>
#import <io.h>
#define EDESTADDRREQ WSAEDESTADDRREQ
#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
#define fork() (-1)
int gettimeofday ( struct timeval *tv, struct timezone *tz);
#else
#import <libc.h>
#import <netdb.h>
#import <netinet/in_systm.h>
#import <netinet/ip.h>
#import <netinet/ip_icmp.h>
#import <netinet/tcp.h>
#endif WIN32
#import <errno.h>
extern int errno;
static MiscINETSocket *handySocket = nil;
static unsigned short in_cksum(unsigned char *cp, int nbytes)
{
unsigned short wa[(nbytes>>1)+1], *wp, result;
long sum;
wp = &wa[0];
memset((char *)wp, 0, sizeof(wp));
memcpy((char *)wp, cp, nbytes);
for (sum = 0; 0 < nbytes; nbytes -= 2)
sum += *wp++;
while (sum >> 16)
sum = (sum >> 16) + (sum & 0xffff);
result = (unsigned short)~sum;
if (result == 0x0)
result = 0xffff;
return result;
}
static int wait_for_socket_read(int sock, unsigned int ms)
{
struct timeval timeout = {ms/1000, (ms%1000)*1000};
fd_set rfds;
int ret;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
while ((ret = select(sock+1, &rfds, NULL, NULL, (ms==0 ? NULL : &timeout))) < 0) {
if (errno == EINTR)
continue;
return -1;
}
return ret;
}
static int get_service_port(const char *service, int type)
{
struct servent *sent;
switch (type) {
case MiscSOCK_DGRAM :
sent = getservbyname((char *)service, "udp");
if (sent == NULL)
sent = getservbyport(atoi(service), "udp");
break;
case MiscSOCK_STREAM:
sent = getservbyname((char *)service, "tcp");
if (sent == NULL)
sent = getservbyport(atoi(service), "tcp");
break;
default :
/* RAW connections to services are not supported. */
errno = ESOCKTNOSUPPORT;
return -1;
}
if (sent == NULL) {
errno = ENOENT;
return -1;
}
return sent->s_port;
}
@implementation MiscINETSocket
#define ECHOPKTLEN 32
- free
{
[self release];
printf("Freeing....\n");
return nil;
}
#ifndef WIN32
+ (int)ping:(MiscINETAddress *)addr timeout:(unsigned int)ms useECHO:(BOOL)aBool
{
if (addr == nil) {
errno = EDESTADDRREQ;
return -1;
}
if (aBool) {
char out_pkt[ECHOPKTLEN], in_pkt[2*ECHOPKTLEN];
int i, ret, in_len = sizeof(in_pkt);
for (i = 0; i < ECHOPKTLEN; i++)
out_pkt[i] = (char)(random()%256);
ret = [MiscINETSocket sendDgram:out_pkt length:sizeof(out_pkt) to:addr service:"echo" timeout:ms retries:0 withReply:in_pkt length:&in_len];
if (ret <= 0)
return ret;
if (in_len != ECHOPKTLEN)
return -1;
for (i = 0; i < ECHOPKTLEN; i++)
if (out_pkt[i] != in_pkt[i])
return -1;
return 1;
} else {
struct {
unsigned char type, code;
unsigned short cksum, id, seq;
unsigned char data[2*ICMP_MINLEN];
} out_pkt = {ICMP_ECHO, 0, 0, getpid() & 0xffff, 1};
struct sockaddr_in sockaddr;
char buffer[sizeof(out_pkt)+64];
int ret;
struct icmp *icmpp;
if (handySocket == nil)
handySocket = [MiscINETSocket alloc];
handySocket = [handySocket init];
handySocket->sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
if (handySocket->sock < 0)
return -1;
handySocket->domain = PF_INET;
handySocket->type = RAW;
out_pkt.cksum = in_cksum((u_char *)&out_pkt, sizeof(out_pkt));
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr = [addr address];
sockaddr.sin_port = 0;
if (sendto(handySocket->sock, &out_pkt, sizeof(out_pkt), 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0)
return -1;
ret = wait_for_socket_read(handySocket->sock, ms);
if (ret <= 0)
return ret;
memset(buffer, '\0', sizeof(buffer));
if (recv(handySocket->sock, buffer, sizeof(buffer), 0) < 0)
return -1;
icmpp = (struct icmp *)(buffer + 4*((struct ip *)buffer)->ip_hl);
if (icmpp->icmp_type == ICMP_ECHOREPLY && icmpp->icmp_id == (getpid() & 0xffff))
return 1;
return -1;
}
}
#endif WIN32
+ runServer:(id)sockets target:(id)target action:(SEL)action fork:(BOOL)fFlag loop:(BOOL)lFlag
{
/* id newSocket;
if (![target respondsTo:action] ||
![sockets isKindOfClass:[MiscSocket class]]) {
errno = EINVAL;
return nil;
}
do {
if ([sockets type] == MiscSOCK_STREAM)
if ([sockets acceptNewConnection:newSocket timeout:0] < 0)
return nil;
if (fFlag) {
switch (fork()) {
case -1:
return nil;
case 0 :
[target perform:action with:newSocket];
exit(0);
}
} else
[target perform:action with:newSocket];
// Why did Chris free here?
// if ([newSocket type] == MiscSOCK_STREAM)
// [socket free];
} while (lFlag);
return self;
}
*/
BOOL isCollection = NO;
fd_set fds;
int fd, nfd;
if (![target respondsTo:action] || sockets == nil) {
errno = EINVAL;
return nil;
}
FD_ZERO(&fds);
if ([sockets isKindOf:[HashTable class]]) {
Class Socket = [MiscSocket class];
for (fd = sizeof(fds)*8; fd--;) {
id obj;
if (![sockets isKey:(void *)fd])
continue;
obj = [sockets valueForKey:(void *)fd];
if ([obj isKindOfClass:Socket] && ![obj isClosed])
FD_SET([obj socket], &fds);
}
isCollection = YES;
} else
if ([sockets isKindOfClass:[MiscSocket class]] && ![sockets isClosed])
FD_SET([sockets socket], &fds);
else
return nil;
for (fd = sizeof(fds)*8; fd--;)
if (FD_ISSET(fd, &fds))
break;
nfd = fd + 1;
if (nfd <= 0) {
errno = EINVAL;
return nil;
}
do {
fd_set rfds;
id sock_obj;
memcpy(&rfds, &fds, sizeof(rfds));
if (select(nfd, &rfds, NULL, NULL, NULL) < 0) {
if (errno == EINTR)
continue;
return nil;
}
for (fd = 0; fd < nfd; fd++) {
id newSocket = nil;
if (!FD_ISSET(fd, &rfds))
continue;
sock_obj = isCollection ? (id)[sockets valueForKey:(void *)fd] : sockets;
if ([sock_obj type] == MiscSOCK_STREAM)
if ([sock_obj acceptNewConnection:newSocket timeout:0] < 0)
return nil;
if (fFlag) {
switch (fork()) {
case -1:
return nil;
case 0 :
[target perform:action with:newSocket];
exit(0);
}
} else
[target perform:action with:newSocket];
// if ([newSocket type] == MiscSOCK_STREAM)
//{printf("Gonna free\n"); // Freeing is ain't good at all
// [newSocket autorelease]; //// [sock_obj free];
//}
}
} while (lFlag);
return self;
}
+ runServerPort:(int *)port type:(int)aType action:(SEL)action fork:(BOOL)fFlag loop:(BOOL)lFlag{
MiscINETSocket* soc = [[self alloc] openServerPort:port type:aType];
return [self runServer:soc target:soc action:action fork:fFlag loop:lFlag];
}
+ sendDgram:(void *)data length:(int)dlen to:(MiscINETAddress *)addr port:(int)portNum
{
struct sockaddr_in sockaddr;
if (addr == nil) {
errno = EDESTADDRREQ;
return nil;
}
if (handySocket == nil)
handySocket = [MiscINETSocket alloc];
handySocket = [handySocket initDomain:PF_INET type:MiscSOCK_DGRAM];
if (handySocket == nil || portNum < 0)
return nil;
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr = [addr address];
sockaddr.sin_port = htons((short)(portNum & 0xffff));
if (sendto(handySocket->sock, data, dlen, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0)
return nil;
return self;
}
+ sendDgram:(void *)data length:(int)dlen to:(MiscINETAddress *)addr service:(const char *)service
{
return [self sendDgram:data length:dlen to:addr port:get_service_port(service, MiscSOCK_DGRAM)];
}
+ (int)sendDgram:(void *)data length:(int)dlen to:(MiscINETAddress *)addr port:(int)portNum timeout:(unsigned int)ms retries:(int)retries withReply:(void *)repl length:(int *)rlen
{
struct sockaddr_in to_addr;
if (addr == nil) {
errno = EDESTADDRREQ;
return -1;
}
if (handySocket == nil)
handySocket = [MiscINETSocket alloc];
handySocket = [handySocket initDomain:PF_INET type:MiscSOCK_DGRAM];
if (handySocket == nil || portNum < 0)
return -1;
to_addr.sin_family = AF_INET;
to_addr.sin_addr = [addr address];
to_addr.sin_port = htons((short)(portNum & 0xffff));
do {
int ret;
if (sendto(handySocket->sock, data, dlen, 0, (struct sockaddr *)&to_addr, sizeof(to_addr)) < 0)
return -1;
ret = wait_for_socket_read(handySocket->sock, ms);
if (ret < 0)
return -1;
if (0 < ret) {
struct sockaddr_in from_addr;
int len = sizeof(from_addr);
ret = recvfrom(handySocket->sock, repl, *rlen, 0, (struct sockaddr *)&from_addr, &len);
if (ret < 0)
return -1;
if (!memcmp(&to_addr, &from_addr, len-sizeof(to_addr.sin_zero))) {
*rlen = ret;
return 1;
}
}
} while (retries-- > 0);
return 0;
}
+ (int)sendDgram:(void *)data length:(int)dlen to:(MiscINETAddress *)addr service:(const char *)service timeout:(unsigned int)ms retries:(int)retries withReply:(void *)repl length:(int *)rlen
{
return [self sendDgram:data length:dlen to:addr port:get_service_port(service, MiscSOCK_DGRAM) timeout:ms retries:retries withReply:repl length:rlen];
}
- init
{
[super init];
if (localAddress != nil)
[localAddress release];
if (remoteAddress != nil)
[remoteAddress release];
localPortNum = remotePortNum = -1;
localAddress = remoteAddress = nil;
return self;
}
- (void)dealloc
{
if (localAddress != nil)
[localAddress release];
if (remoteAddress != nil)
[remoteAddress release];
[super dealloc];
}
- initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
[coder decodeValuesOfObjCTypes:"ii", &localPortNum, &remotePortNum];
localAddress = [[coder decodeObject] retain];
remoteAddress = [[coder decodeObject] retain];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[super encodeWithCoder:coder];
[coder encodeValuesOfObjCTypes:"ii", &localPortNum, &remotePortNum];
[coder encodeObject:localAddress];
[coder encodeObject:remoteAddress];
}
- copy
{
return [self copyWithZone:[self zone]];
}
- copyWithZone:(NSZone*)zone
{
MiscINETSocket *copy = [[[self class] allocWithZone:zone] init];
copy->localAddress = [[localAddress copyWithZone:zone] retain];
copy->remoteAddress = [[remoteAddress copyWithZone:zone] retain];
return copy;
}
- (int)acceptNewConnection:(MiscINETSocket *)newSocket timeout:(unsigned int)ms
{
int ret;
if (type != MiscSOCK_STREAM) {
errno = EINVAL;
return -1;
}
ret = wait_for_socket_read(sock, ms);
if (0 < ret) {
struct sockaddr_in sockaddr;
int len = sizeof(sockaddr);
ret = accept(sock, (struct sockaddr *)&sockaddr, &len);
if (0 <= ret) {
newSocket = [newSocket isKindOfClass:[MiscINETSocket class]] ?
[newSocket init] : [MiscINETSocket new];
newSocket->sock = ret;
newSocket->domain = PF_INET;
newSocket->type = MiscSOCK_STREAM;
ret = 1;
}
// else
// newSocket = nil;
}
return ret;
}
- connectTo:(MiscINETAddress *)addr port:(int)portNum type:(int)aType
{
struct sockaddr_in sockaddr;
if (addr == nil) {
errno = EDESTADDRREQ;
return [self free];
}
self = [self initDomain:PF_INET type:aType];
if (self == nil || portNum < 0)
return [self free];
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr = [addr address];
sockaddr.sin_port = htons((short)(portNum & 0xffff));
if (connect(sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0)
return [self free];
return self;
}
- connectTo:(MiscINETAddress *)addr service:(const char *)service type:(int)aType
{
return [self connectTo:addr port:get_service_port(service, aType) type:aType];
}
- openServerPort:(int *)portNum type:(int)aType
{
struct sockaddr_in sockaddr;
int on = 1, old_errno = errno;
self = [self initDomain:PF_INET type:aType];
if (self == nil || *portNum < 0)
return [self free];
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
errno = old_errno;
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons((short)(*portNum & 0xffff));
sockaddr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0)
return [self free];
if (aType == MiscSOCK_STREAM)
listen(sock, 2);
return self;
}
- openServerService:(const char *)service type:(int)aType
{
int port = get_service_port(service, aType);
if (port < 0)
return [self free];
return [self openServerPort:&port type:aType];
}
- (int)receiveData:(void *)data length:(int *)dlen
{
return [self receiveData:data length:dlen timeout:0 from:nil port:NULL];
}
- (int)receiveData:(void *)data length:(int *)dlen timeout:(unsigned int)ms from:(MiscINETAddress *)addr port:(int *)port
{
int ret;
ret = wait_for_socket_read(sock, ms);
if (0 <= ret) {
struct sockaddr_in sockaddr;
int len = sizeof(sockaddr);
ret = recvfrom(sock, data, *dlen, 0, (struct sockaddr *)&sockaddr, &len);
if (0 == ret) {
[self close];
errno = EPIPE;
ret = -1;
} else if (0 < ret) {
[addr initTo:sockaddr.sin_addr];
*dlen = ret;
if (port != NULL)
*port = (int)ntohs(sockaddr.sin_port);
ret = 1;
}
}
return ret;
}
- (int)receiveData:(void *)data length:(int *)dlen timeout:(unsigned int)ms toNext:(unsigned char)sentinel
{
struct timeval t1, t2;
int i, ret;
if (type != MiscSOCK_STREAM) {
errno = EINVAL;
return -1;
}
gettimeofday(&t1, NULL);
for (i = *dlen, *dlen = 0; i--;) {
if (0 < ms) {
int ms_left;
gettimeofday(&t2, NULL);
ms_left = ms - 1000*(t2.tv_sec-t1.tv_sec) + (t2.tv_usec-t1.tv_usec)/1000;
if (ms_left < 1)
return 0;
ret = wait_for_socket_read(sock, ms_left);
if (ret <= 0)
return ret;
}
ret = read(sock, data, 1);
if (ret < 0)
return -1;
else if (0 == ret) {
[self close];
errno = EPIPE;
return -1;
}
(*dlen)++;
if (*(unsigned char *)data == sentinel)
return 1;
data++;
}
return 1;
}
- (int)receiveData:(NSMutableData *)data
{
int length = [data length];
int ret = [self receiveData:[data mutableBytes] length:&length];
if (ret>0)
[data setLength:length];
return ret;
}
- sendData:(void *)data length:(int)dlen
{
while (0 < dlen) {
int ret = write(sock, data, dlen); if (ret <= 0) {
if (errno == EINTR)
continue;
else if (errno == EPIPE || errno == 0) {
[self close];
return nil;
}
return nil;
}
dlen -= ret;
data += ret;
}
return self;
}
- sendData:(NSData *)data
{
return [self sendData:(void *)[data bytes] length:[data length]];
}
- shutdownLocalEnd
{
if (localAddress != nil)
[localAddress release];
localPortNum = -1;
localAddress = nil;
return (shutdown(sock, 1) < 0 ? nil : self);
}
- shutdownRemoteEnd
{
if (remoteAddress != nil)
[remoteAddress release];
remotePortNum = -1;
remoteAddress = nil;
return (shutdown(sock, 0) < 0 ? nil : self);
}
- enableDebug:(BOOL)aBool
{
int on = aBool;
if (![self isClosed]) {
int size = sizeof(on), old_errno = errno;
setsockopt(sock, SOL_SOCKET, SO_DEBUG, (char *)&on, size);
errno = old_errno;
}
return self;
}
- (BOOL)debugEnabled
{
int on = NO;
if (![self isClosed]) {
int size = sizeof(on), old_errno = errno;
getsockopt(sock, SOL_SOCKET, SO_DEBUG, (char *)&on, &size);
errno = old_errno;
}
return (BOOL)on;
}
- enableDelay:(BOOL)aBool
{
int on = aBool;
if (![self isClosed]) {
int size = sizeof(on), old_errno = errno;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&on, size);
errno = old_errno;
}
return self;
}
- (BOOL)delayEnabled
{
int on = YES;
if (![self isClosed]) {
int size = sizeof(on), old_errno = errno;
getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&on, &size);
errno = old_errno;
}
return (BOOL)on;
}
- enableKeepAlive:(BOOL)aBool
{
int on = aBool;
if (![self isClosed]) {
int size = sizeof(on), old_errno = errno;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, size);
errno = old_errno;
}
return self;
}
- (BOOL)keepAliveEnabled
{
int on = NO;
if (![self isClosed]) {
int size = sizeof(on), old_errno = errno;
getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, &size);
errno = old_errno;
}
return (BOOL)on;
}
- setLingerTime:(int)secs
{
if (![self isClosed]) {
struct linger ling;
int size = sizeof(ling), old_errno = errno;
ling.l_onoff = 0 < secs ? 1 : 0;
ling.l_linger = 0 < secs ? secs : 0;
setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&ling, size);
errno = old_errno;
}
return self;
}
- (int)lingerTime
{
int time = 0;
if (![self isClosed]) {
struct linger ling;
int size = sizeof(ling), old_errno = errno;
getsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&ling, &size);
errno = old_errno;
if (ling.l_onoff)
time = ling.l_linger;
}
return time;
}
- (BOOL)dataAvailable
{
int old_errno = errno;
BOOL avail = NO;
if (![self isClosed]) {
struct timeval timeout = {0, 0};
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
if (0 < select(sock+1, &rfds, NULL, NULL, &timeout))
avail = YES;
}
errno = old_errno;
return avail;
}
- (MiscINETAddress *)localAddress
{
if (localAddress == nil) {
struct sockaddr_in sockaddr;
int len = sizeof(sockaddr);
if (getsockname(sock, (struct sockaddr *)&sockaddr, &len) < 0)
return nil;
localAddress = [[[MiscINETAddress allocWithZone:[self zone]] initTo:sockaddr.sin_addr] retain];
if (localAddress != nil)
localPortNum = ntohs(sockaddr.sin_port & 0xffff);
}
return localAddress;
}
- (int)localPortNum
{
if (localPortNum == -1)
[self localAddress];
return localPortNum;
}
- (MiscINETAddress *)remoteAddress
{
if (remoteAddress == nil) {
struct sockaddr_in sockaddr;
int len = sizeof(sockaddr);
if (getpeername(sock, (struct sockaddr *)&sockaddr, &len) < 0)
return nil;
remoteAddress = [[[MiscINETAddress allocWithZone:[self zone]] initTo:sockaddr.sin_addr] retain];
if (remoteAddress != nil)
remotePortNum = ntohs(sockaddr.sin_port & 0xffff);
}
return remoteAddress;
}
- (int)remotePortNum
{
if (remotePortNum == -1)
[self remoteAddress];
return remotePortNum;
}
- (int)socketError
{
int err = 0;
if (![self isClosed]) {
int size = sizeof(err), old_errno = errno;
getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &size);
errno = old_errno;
}
return err;
}
@end
// Alternate info to get this working on NT
// (I haven't messed with it yet --don)
/*
From: Aleksey Sudakov <zander@conextions.com>
Well, all that you gotta do is to implement negotiation with WinSock.dll in +(void)initialize and change couple of methods, like ioctl sould become ioctlsocket; errno -> h_errno; read -> recv; write -> send, etc. Use #define macro. It should be something like this
#if defined(__MACH__)
#define MISC_ioctl(x,y,z) ioctl(x,y,z)
#define MISC_EWOULDBLOCK EWOULDBLOCK
#define MISC_EINPROGRESS EINPROGRESS
#define MISC_errno errno
#define MISC_read(x,y,z) read(x,y,z)
#define MISC_write(x,y,z) write(x,y,z)
#define INVALID_SOCKET (-1)
#define SOCKET_ERROR (-1)
#elif defined(__WIN__)
#define MISC_ioctl(x,y,z) ioctlsocket((SOCKET)x,(long)y,(u_long FAR *)z)
#define MISC_EWOULDBLOCK WSAEWOULDBLOCK
#define MISC_EINPROGRESS WSAEINPROGRESS
#define MISC_errno h_errno
#define MISC_read(x,y,z) recv((SOCKET)x, (char FAR *)y, (int)z, 0)
#define MISC_write(x,y,z) send((SOCKET)x, (const char FAR *)y, (int)z, 0)
#define bcopy(src,dest,nb) memcpy(dest,src,nb)
#define bzero(ptr,nb) memset(ptr,0,nb)
#endif
*/These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.