/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3.0 only,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* version 3.0 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3.0 along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <errno.h>
#include <string.h>
#import "ObjFW.h"
#import "ObjFWTest.h"
@interface OFSPXStreamSocketTests: OTTestCase
{
OFSPXStreamSocket *_sockServer;
OFSocketAddress _addrServer;
}
@end
@interface SPXStreamSocketDelegate: OFObject <OFSPXStreamSocketDelegate>
{
@public
OFStreamSocket *_expectedServerSocket;
OFSPXStreamSocket *_expectedClientSocket;
uint32_t _expectedNetwork;
unsigned char _expectedNode[IPX_NODE_LEN];
uint16_t _expectedPort;
bool _accepted;
bool _connected;
}
@end
@implementation OFSPXStreamSocketTests
- (void)setUp
{
const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
_sockServer = [[OFSPXStreamSocket alloc] init];
@try {
_addrServer = [_sockServer bindToNetwork: 0
node: zeroNode
port: 0];
} @catch (OFBindSocketFailedException *e) {
switch (e.errNo) {
case EAFNOSUPPORT:
OTSkip(@"IPX unsupported");
case ESOCKTNOSUPPORT:
case EPROTONOSUPPORT:
OTSkip(@"SPX unsupported");
case EADDRNOTAVAIL:
OTSkip(@"IPX not configured");
default:
@throw e;
}
}
}
- (void)dealloc
{
[_sockServer release];
[super dealloc];
}
- (void)testSPXStreamSocket
{
OFSPXStreamSocket *sockClient, *sockAccepted;
const OFSocketAddress *addrAccepted;
uint32_t network;
unsigned char node[IPX_NODE_LEN], node2[IPX_NODE_LEN];
uint16_t port;
OFDictionary *networkInterfaces;
char buffer[5];
sockClient = [OFSPXStreamSocket socket];
network = OFSocketAddressIPXNetwork(&_addrServer);
OFSocketAddressGetIPXNode(&_addrServer, node);
port = OFSocketAddressIPXPort(&_addrServer);
[_sockServer listen];
/*
* Find any network interface with IPX and send to it. Any should be
* fine since we bound to 0.0.
*/
networkInterfaces = [OFSystemInfo networkInterfaces];
for (OFString *name in networkInterfaces) {
OFNetworkInterface interface = [networkInterfaces
objectForKey: name];
OFData *addresses = [interface
objectForKey: OFNetworkInterfaceIPXAddresses];
if (addresses.count == 0)
continue;
network = OFSocketAddressIPXNetwork([addresses itemAtIndex: 0]);
OFSocketAddressGetIPXNode([addresses itemAtIndex: 0], node);
}
[sockClient connectToNetwork: network node: node port: port];
sockAccepted = [_sockServer accept];
[sockAccepted writeBuffer: "Hello" length: 5];
/* Test reassembly (this would not work with OFSPXSocket) */
OTAssertEqual([sockClient readIntoBuffer: buffer length: 2], 2);
OTAssertEqual([sockClient readIntoBuffer: buffer + 2 length: 3], 3);
OTAssertEqual(memcmp(buffer, "Hello", 5), 0);
addrAccepted = sockAccepted.remoteAddress;
OFSocketAddressGetIPXNode(addrAccepted, node2);
OTAssertEqual(memcmp(node, node2, IPX_NODE_LEN), 0);
}
- (void)testAsyncSPXStreamSocket
{
SPXStreamSocketDelegate *delegate =
[[[SPXStreamSocketDelegate alloc] init] autorelease];
uint32_t network;
unsigned char node[IPX_NODE_LEN];
uint16_t port;
OFDictionary *networkInterfaces;
OFSPXStreamSocket *sockClient;
delegate->_expectedServerSocket = _sockServer;
_sockServer.delegate = delegate;
sockClient = [OFSPXStreamSocket socket];
delegate->_expectedClientSocket = sockClient;
sockClient.delegate = delegate;
[_sockServer listen];
[_sockServer asyncAccept];
network = OFSocketAddressIPXNetwork(&_addrServer);
OFSocketAddressGetIPXNode(&_addrServer, node);
port = OFSocketAddressIPXPort(&_addrServer);
/*
* Find any network interface with IPX and send to it. Any should be
* fine since we bound to 0.0.
*/
networkInterfaces = [OFSystemInfo networkInterfaces];
for (OFString *name in networkInterfaces) {
OFNetworkInterface interface = [networkInterfaces
objectForKey: name];
OFData *addresses = [interface
objectForKey: OFNetworkInterfaceIPXAddresses];
if (addresses.count == 0)
continue;
network = OFSocketAddressIPXNetwork([addresses itemAtIndex: 0]);
OFSocketAddressGetIPXNode([addresses itemAtIndex: 0], node);
}
delegate->_expectedNetwork = network =
OFSocketAddressIPXNetwork(&_addrServer);
OFSocketAddressGetIPXNode(&_addrServer, node);
memcpy(delegate->_expectedNode, node, IPX_NODE_LEN);
delegate->_expectedPort = port = OFSocketAddressIPXPort(&_addrServer);
@try {
[sockClient asyncConnectToNetwork: network
node: node
port: port];
[[OFRunLoop mainRunLoop] runUntilDate:
[OFDate dateWithTimeIntervalSinceNow: 2]];
OTAssertTrue(delegate->_accepted);
OTAssertTrue(delegate->_connected);
} @catch (OFObserveKernelEventsFailedException *e) {
/*
* Make sure it doesn't stay in the run loop and throws again
* next time we run the run loop.
*/
[sockClient cancelAsyncRequests];
[_sockServer cancelAsyncRequests];
switch (e.errNo) {
case ENOTSOCK:
OTSkip(@"select() not supported for SPX");
default:
@throw e;
}
}
}
@end
@implementation SPXStreamSocketDelegate
- (bool)socket: (OFStreamSocket *)sock
didAcceptSocket: (OFStreamSocket *)accepted
exception: (id)exception
{
OFEnsure(!_accepted);
_accepted = (sock == _expectedServerSocket && accepted != nil &&
exception == nil);
if (_accepted && _connected)
[[OFRunLoop mainRunLoop] stop];
return false;
}
- (void)socket: (OFSPXStreamSocket *)sock
didConnectToNetwork: (uint32_t)network
node: (const unsigned char [IPX_NODE_LEN])node
port: (uint16_t)port
exception: (id)exception
{
OFEnsure(!_connected);
_connected = (sock == _expectedClientSocket &&
network == _expectedNetwork &&
memcmp(node, _expectedNode, IPX_NODE_LEN) == 0 &&
port == _expectedPort && exception == nil);
if (_accepted && _connected)
[[OFRunLoop mainRunLoop] stop];
}
@end