ObjFW  Diff

Differences From Artifact [5573556d55]:

To Artifact [11bd6fb256]:

  • File src/OFDDPSocket.m — part of check-in [9c2f20e736] at 2022-11-03 00:14:27 on branch trunk — OFDDPSocket: Don't include the type with the data

    This seems to be an oddity limited to OSes that have implemented DDP
    exclusively for netatalk, while macOS and Windows don't include it with
    the data.

    While on macOS it's possible to achieve the previous behavior via some
    hacks, this is impossible on Windows, so the proper approach is to
    handle it like everybody else: Specify the protocol type when binding
    and only handle packets of the correct protocol type. (user: js, size: 7082) [annotate] [blame] [check-ins using] [more...]


23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46




47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62
63



64
65
66
67
68
69
70
71

72
73

74

75
76
77
78
79

80
81
82
83
84
85
86
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81
82
83

84
85
86
87
88
89
90
91
92
93
94
95
96
97







+

















+
+
+
+







+










+
+
+







-
+


+
-
+





+








#import "OFDDPSocket.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"

#import "OFAlreadyConnectedException.h"
#import "OFBindDDPSocketFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFNotOpenException.h"
#import "OFReadFailedException.h"
#import "OFWriteFailedException.h"

#ifdef OF_HAVE_NETAT_APPLETALK_H
# include <netat/ddp.h>
# include <sys/ioctl.h>

/* Unfortulately, there is no struct for the following in userland headers */
struct ATInterfaceConfig {
	char interfaceName[16];
	unsigned int flags;
	struct at_addr address, router;
	unsigned short netStart, netEnd;
	at_nvestr_t zoneName;
};
#endif

#ifndef ATPROTO_BASE
# define ATPROTO_BASE 0
#endif

@implementation OFDDPSocket
@dynamic delegate;

- (OFSocketAddress)bindToNetwork: (uint16_t)network
			    node: (uint8_t)node
			    port: (uint8_t)port
		    protocolType: (uint8_t)protocolType
{
#ifdef OF_MACOS
	const int one = 1;
	struct ATInterfaceConfig config = { { 0 } };
#endif
	OFSocketAddress address;
#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
	int flags;
#endif

	if (protocolType == 0)
		@throw [OFInvalidArgumentException exception];

	if (_socket != OFInvalidSocketHandle)
		@throw [OFAlreadyConnectedException exceptionWithSocket: self];

	address = OFSocketAddressMakeAppleTalk(network, node, port);

#ifdef OF_MACOS
	if ((_socket = socket(address.sockaddr.at.sat_family,
	    SOCK_RAW | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
	    SOCK_RAW | SOCK_CLOEXEC, protocolType)) == OFInvalidSocketHandle)
#else
	if ((_socket = socket(address.sockaddr.at.sat_family,
	    SOCK_DGRAM | SOCK_CLOEXEC, ATPROTO_BASE + protocolType)) ==
	    SOCK_DGRAM | SOCK_CLOEXEC, 0)) == OFInvalidSocketHandle)
	    OFInvalidSocketHandle)
#endif
		@throw [OFBindDDPSocketFailedException
		    exceptionWithNetwork: network
				    node: node
				    port: port
			    protocolType: protocolType
				  socket: self
				   errNo: OFSocketErrNo()];

	_canBlock = true;

#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
131

132
133
134
135
136
137
138
139



140
141
142
143
144

145
146
147
148
149
150




151
152
153
154














155

156
157
158
159
160
161

162
163

164






165
166
167
168
169
170

171
172
173
174
175
176
177
178




179
180
181
182


183
184

185
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232


233
234
235
236
237
238
239
240
241
242
243
244
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150



151
152
153

154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

187
188
189
190
191
192

193
194

195
196
197
198
199
200
201
202
203
204
205
206
207

208
209
210
211
212
213



214
215
216
217
218



219
220
221

222
223
224
225
226
227
228

229

230
231
232
233
234
235
236
237
238


239
240
241
242
243















244
245
246
247
248
249
250


251
252
253
254
255
256
257
258
259
260
261
262
263
264







+



















+












+





-
-
-
+
+
+
-




+






+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+





-
+

-
+

+
+
+
+
+
+





-
+





-
-
-
+
+
+
+

-
-
-
+
+

-
+






-

-
+








-
-





-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







-
-
+
+












		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindDDPSocketFailedException
		    exceptionWithNetwork: network
				    node: node
				    port: port
			    protocolType: protocolType
				  socket: self
				   errNo: errNo];
	}

	memset(&address, 0, sizeof(address));
	address.family = OFSocketAddressFamilyAppleTalk;
	address.length = (socklen_t)sizeof(address.sockaddr);

	if (OFGetSockName(_socket, (struct sockaddr *)&address.sockaddr,
	    &address.length) != 0) {
		int errNo = OFSocketErrNo();

		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindDDPSocketFailedException
		    exceptionWithNetwork: network
				    node: node
				    port: port
			    protocolType: protocolType
				  socket: self
				   errNo: errNo];
	}

	if (address.sockaddr.at.sat_family != AF_APPLETALK) {
		closesocket(_socket);
		_socket = OFInvalidSocketHandle;

		@throw [OFBindDDPSocketFailedException
		    exceptionWithNetwork: network
				    node: node
				    port: port
			    protocolType: protocolType
				  socket: self
				   errNo: EAFNOSUPPORT];
	}

#ifdef OF_MACOS
	if (setsockopt(_socket, ATPROTO_NONE, DDP_HDRINCL, &one, sizeof(one)) !=
	    0 ||
	    ioctl(_socket, _IOWR('a', 2, struct ATInterfaceConfig), &config) !=
	if (setsockopt(_socket, ATPROTO_NONE, DDP_STRIPHDR, &one,
	    sizeof(one)) != 0 || ioctl(_socket, _IOWR('a', 2,
	    struct ATInterfaceConfig), &config) != 0)
	    0)
		@throw [OFBindDDPSocketFailedException
		    exceptionWithNetwork: network
				    node: node
				    port: port
			    protocolType: protocolType
				  socket: self
				   errNo: OFSocketErrNo()];

	OFSocketAddressSetAppleTalkNetwork(&address, config.address.s_net);
	OFSocketAddressSetAppleTalkNode(&address, config.address.s_node);
#endif

#if !defined(OF_MACOS) && !defined(OF_WINDOWS)
	_protocolType = protocolType;
#endif

	return address;
}

/*
 * Everybody but macOS and Windows is probably using a netatalk-compatible
 * implementation, which includes the protocol type as the first byte of the
 * data instead of considering it part of the header.
 *
 * The following overrides prepend the protocol type when sending and compare
 * and strip it when receiving.
 *
 * Unfortunately, the downside of this is that the only way to handle receiving
 * a packet with the wrong protocol type is to throw an exception with errNo
 * ENOMSG, while macOS and Windows just filter those out in the kernel.
 * Returning 0 would mean this is indistinguishable from an empty packet, so it
 * has to be an exception.
 */
#ifdef OF_MACOS
#if !defined(OF_MACOS) && !defined(OF_WINDOWS)
- (size_t)receiveIntoBuffer: (void *)buffer
		     length: (size_t)length
		     sender: (OFSocketAddress *)sender
{
	ssize_t ret;
	at_ddp_t header;
	uint8_t protocolType;
	struct iovec iov[2] = {
		{ &header, offsetof(at_ddp_t, type) },
		{ &protocolType, 1 },
		{ buffer, length }
	};
	struct msghdr msg = {
		.msg_name = (struct sockaddr *)&sender->sockaddr,
		.msg_namelen = (socklen_t)sizeof(sender->sockaddr),
		.msg_iov = iov,
		.msg_iovlen = 2
	};

	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	if ((ret = readv(_socket, iov, 2)) < 0)
	if ((ret = recvmsg(_socket, &msg, 0)) < 0)
		@throw [OFReadFailedException
		    exceptionWithObject: self
			requestedLength: length
				  errNo: OFSocketErrNo()];

	*sender = OFSocketAddressMakeAppleTalk(
	    header.src_net[0] << 8 | header.src_net[1], header.src_node,
	    header.src_socket);
	if (ret < 1 || protocolType != _protocolType)
		@throw [OFReadFailedException exceptionWithObject: self
						  requestedLength: length
							    errNo: ENOMSG];

	ret -= offsetof(at_ddp_t, type);
	if (ret < 0)
		return 0;
	sender->length = msg.msg_namelen;
	sender->family = OFSocketAddressFamilyAppleTalk;

	return ret;
	return ret - 1;
}

- (void)sendBuffer: (const void *)buffer
	    length: (size_t)length
	  receiver: (const OFSocketAddress *)receiver
{
	at_ddp_t header = { 0 };
	struct iovec iov[2] = {
		{ &header, offsetof(at_ddp_t, type) },
		{ &_protocolType, 1 },
		{ (void *)buffer, length }
	};
	struct msghdr msg = {
		.msg_name = (struct sockaddr *)&receiver->sockaddr,
		.msg_namelen = receiver->length,
		.msg_iov = iov,
		.msg_iovlen = 2
	};
	size_t packetLength = length + offsetof(at_ddp_t, type);
	uint16_t net;
	ssize_t bytesWritten;

	if (_socket == OFInvalidSocketHandle)
		@throw [OFNotOpenException exceptionWithObject: self];

	if (packetLength > DDP_DATAGRAM_SIZE)
		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: 0
							     errNo: EMSGSIZE];

	net = OFSocketAddressAppleTalkNetwork(receiver);

	header.length_H = (packetLength >> 8) & 3;
	header.length_L = packetLength & 0xFF;
	header.dst_net[0] = net >> 8;
	header.dst_net[1] = net & 0xFF;
	header.dst_node = OFSocketAddressAppleTalkNode(receiver);
	header.dst_socket = OFSocketAddressAppleTalkPort(receiver);

	if ((bytesWritten = sendmsg(_socket, &msg, 0)) < 0)
		@throw [OFWriteFailedException
		    exceptionWithObject: self
			requestedLength: length
			   bytesWritten: 0
				  errNo: OFSocketErrNo()];

	if ((size_t)bytesWritten != packetLength) {
		bytesWritten -= offsetof(at_ddp_t, type);
	if ((size_t)bytesWritten != length + 1) {
		bytesWritten--;

		if (bytesWritten < 0)
			bytesWritten = 0;

		@throw [OFWriteFailedException exceptionWithObject: self
						   requestedLength: length
						      bytesWritten: bytesWritten
							     errNo: 0];
	}
}
#endif
@end