sctp-socket 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 319cf8cad773727889aed0c51508d4febfa4e145f9572ef2cdc265bdfc4c6ef2
4
- data.tar.gz: aa085297b675c4f8a1468d689868a25ff6e9b9f5c954324801146b3878bdb150
3
+ metadata.gz: 0475ccb484ff3e6e758c037edf11b16fdb833e49aee961a98532dfb67c84f799
4
+ data.tar.gz: 70f7add0defeeb84a1ab6bb38ed3aeabb5f643bb05dea06a00a11c1934f588ed
5
5
  SHA512:
6
- metadata.gz: a26f8069a0034447ceadd4bb5e59af079da161e4ed0d2620de9df1809e072284fa2fc7aea0799f8f1d17f2da74de744ebc4fec399f21f93e0afa713092b5e0ca
7
- data.tar.gz: 5267896027a79534c91c52396528654a9f046437ccf437be392d1609fbee72980cfd18116db8e85453048976a990084321089d6159822a5453d4ba155163581f
6
+ metadata.gz: e5e40484afdf170570c38ab9331c99ae4a6f42ba217d97e91ecd5bfbf9296c2ef95f219498fba13b524ad08a1f00271de8be6f358f2982a4c5e45fdc190f0951
7
+ data.tar.gz: f9b9dac418aeae1c79916e732d7310049812c2456c0f4f1c578641f145c2c72b73fbceb655954ad99b03c3c69a890cab9a7455930199902a40aed9de239754a8
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,4 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: djberg96
4
+ open_collective: daniel-berger
data/CHANGES.md CHANGED
@@ -1,3 +1,27 @@
1
+ ## 0.0.7 - 28-May-2024
2
+ * Added the recvv method.
3
+ * The getlocalnames and getpeernames methods now accept optional fileno and
4
+ association ID arguments.
5
+ * The peeloff method now returns the peeled off fileno and no longer modifies
6
+ the receiver, so I dropped the exclamation point from the method name.
7
+ * Added the get_subscriptions method.
8
+ * Changed bind method to bindx and connect method to connectx. I may try to
9
+ subclass Socket someday so I didn't want a conflict, and this more closely
10
+ matches the underlying function name anyway.
11
+ * Changed the sock_fd method to fileno.
12
+ * Changed the default backlog from 1024 to 128 for the listen method.
13
+ * Updated comments and documentation.
14
+ * Added more specs.
15
+
16
+ ## 0.0.6 - 24-May-2024
17
+ * Fixup the sendv method and add some documentation.
18
+ * Added documentation to the get_status method.
19
+ * Update the example server and client code, including comments for how to
20
+ setup multiple dummy IP addresses locally for testing.
21
+ * Some warning cleanup and build improvements.
22
+ * Added SCTP_BINDX constants.
23
+ * Started adding some real specs.
24
+
1
25
  ## 0.0.5 - 15-Dec-2021
2
26
  * Add handling for Linux platforms that don't support the sctp_sendv function
3
27
  and/or the SCTP_SEND_FAILED_EVENT notification.
data/README.md CHANGED
@@ -2,18 +2,20 @@
2
2
 
3
3
  A Ruby interface for SCTP sockets.
4
4
 
5
- WARNING: THIS IS CURRENTLY AN ALPHA PRODUCT. NOT RECOMMENDED FOR PRODUCTION USE AT THIS TIME.
6
-
7
5
  ## Prerequisites
8
6
 
9
7
  You will need the sctp development headers installed.
10
8
 
11
- On some systems, such as RHEL8, you may need to enable the sctp module.
9
+ On some systems, such as RHEL8 or later, you may need to enable the sctp module.
12
10
 
13
11
  ## Installation
14
12
 
15
13
  `gem install sctp-socket`
16
14
 
15
+ ## Installing the Trusted Cert
16
+
17
+ `gem cert --add <(curl -Ls https://raw.githubusercontent.com/djberg96/sctp-socket/main/certs/djberg96_pub.pem)`
18
+
17
19
  ## About SCTP
18
20
 
19
21
  The Stream Control Transmission Protocol (SCTP) is a message oriented, reliable
@@ -37,7 +39,7 @@ require 'sctp/socket'
37
39
  begin
38
40
  port = 62324
39
41
  socket = SCTP::Socket.new
40
- socket.bind(:port => port, :addresses => ['10.0.5.4', '10.0.6.4'])
42
+ socket.bindx(:port => port, :addresses => ['10.0.5.4', '10.0.6.4'])
41
43
  socket.set_initmsg(:output_streams => 5, :input_streams => 5, :max_attempts => 4)
42
44
  socket.subscribe(:data_io => true)
43
45
  socket.listen
@@ -62,8 +64,7 @@ end
62
64
  Currently this has only been developed and tested on Linux. Other platforms
63
65
  will probably only be supported via community contributions.
64
66
 
65
- On Ubuntu 20 and possibly other Linux variants, the sendv method is not
66
- available. Use the sendmsg method instead.
67
+ The sendv method is not available on some Linux variants. Use the sendmsg method instead.
67
68
 
68
69
  Please report any issues on the github project page.
69
70
 
@@ -81,7 +82,7 @@ Apache-2.0
81
82
 
82
83
  ## Copyright
83
84
 
84
- (C) 2020, Daniel J. Berger
85
+ (C) 2020-2024, Daniel J. Berger
85
86
  Al Rights Reserved
86
87
 
87
88
  ## Author
@@ -1,11 +1,41 @@
1
1
  require 'socket'
2
2
  require 'sctp/socket'
3
3
 
4
+ # Adjust as needed. Server server_example.rb for creating
5
+ # fake network interfaces for testing.
6
+ addresses = ['1.1.1.1', '1.1.1.2']
7
+
4
8
  begin
5
9
  port = 62324
6
10
  socket = SCTP::Socket.new
7
- bytes_sent = socket.sendmsg(:message => "Hello World!", :addresses => ['127.0.0.1'], :port => port, :stream => 2)
8
- p bytes_sent
11
+
12
+ # Optional, but could bind to a subset of available addresses
13
+ p socket.bindx(:addresses => addresses)
14
+
15
+ # Initial connection
16
+ p socket.connectx(:addresses => addresses, :port => port)
17
+ p socket.get_status
18
+
19
+ # Try a sendv
20
+ p socket.sendv(:message => ["Hello ", "World!"])
21
+
22
+ # Send messages on separate streams of the same connection
23
+ arr = []
24
+
25
+ 0.upto(4) do |n|
26
+ arr << Thread.new do |t|
27
+ puts "Stream: #{n}"
28
+ bytes_sent = socket.sendmsg(
29
+ :message => "Hello World: #{n+1}",
30
+ :addresses => addresses.shuffle,
31
+ :stream => n,
32
+ :port => port
33
+ )
34
+ puts "Bytes Sent: #{bytes_sent}"
35
+ end
36
+ end
37
+
38
+ arr.map(&:join)
9
39
  ensure
10
40
  socket.close if socket
11
41
  end
@@ -1,11 +1,31 @@
1
1
  require 'sctp/socket'
2
2
 
3
+ # rake compile + ruby -Ilib to run local version
4
+ puts "VERSION: #{SCTP::Socket::VERSION}"
5
+
6
+ # To test multiple IP addresses locally:
7
+ #
8
+ # sudo apt install iproute2
9
+ # Add 'dummy' to /etc/modules
10
+ #
11
+ # sudo ip link add dummy1 type dummy
12
+ # sudo ip link add dummy2 type dummy
13
+ #
14
+ # sudo ip addr add 1.1.1.1/24 dev dummy1
15
+ # sudo ip addr add 1.1.1.2/24 dev dummy2
16
+ #
17
+ # sudo ip link set dummy1 up
18
+ # sudo ip link set dummy2 up
19
+
3
20
  # Adjust IP addresses as needed
21
+ addresses = ['1.1.1.1', '1.1.1.2']
22
+
4
23
  begin
5
24
  port = 62324
6
25
  socket = SCTP::Socket.new
7
- socket.bind(:port => port, :addresses => ['10.0.5.5', '10.0.6.5'])
26
+ socket.bindx(:port => port, :addresses => addresses)
8
27
  socket.set_initmsg(:output_streams => 5, :input_streams => 5, :max_attempts => 4)
28
+ socket.subscribe(:data_io => true, :shutdown => true, :send_failure => true, :partial_delivery => true)
9
29
  socket.listen
10
30
 
11
31
  while true
data/ext/sctp/extconf.rb CHANGED
@@ -1,7 +1,31 @@
1
1
  require 'mkmf'
2
2
 
3
- have_header('netinet/sctp.h')
3
+ dir_config('sctp')
4
+
5
+ unless have_header('netinet/sctp.h')
6
+ os = IO.readlines('/etc/os-release').first.split('=').last
7
+ msg = "\nSCTP HEADERS NOT FOUND. PLEASE INSTALL THEM FIRST LIKE SO:\n\n"
8
+
9
+ if os =~ /red|fedora|centos/i
10
+ msg << "#####################################################################################\n"
11
+ msg << "# dnf install lksctp-tools #\n"
12
+ msg << "# dnf install kernel-modules-extra #\n"
13
+ msg << "# #\n"
14
+ msg << "# sed -e '/blacklist sctp/s/^b/#b/g' -i /etc/modprobe.d/sctp-blacklist.conf #\n"
15
+ msg << "# sed -e '/blacklist sctp/s/^b/#b/g' -i /etc/modprobe.d/sctp_diag-blacklist.conf #\n"
16
+ msg << "# #\n"
17
+ msg << "# sudo systemctl restart systemd-modules-load.service #\n"
18
+ msg << "#####################################################################################\n"
19
+ else
20
+ msg << "sudo apt-get install libsctp-dev lksctp-tools\n\n"
21
+ end
22
+
23
+ warn msg
24
+ exit
25
+ end
26
+
4
27
  have_library('sctp')
5
28
  have_func('sctp_sendv', 'netinet/sctp.h')
29
+ have_func('sctp_recvv', 'netinet/sctp.h')
6
30
  have_struct_member('struct sctp_event_subscribe', 'sctp_send_failure_event', 'netinet/sctp.h')
7
31
  create_makefile('sctp/socket')
data/ext/sctp/socket.c CHANGED
@@ -21,32 +21,17 @@ VALUE v_sctp_status_struct;
21
21
  VALUE v_sctp_rtoinfo_struct;
22
22
  VALUE v_sctp_associnfo_struct;
23
23
  VALUE v_sctp_default_send_params_struct;
24
+ VALUE v_sctp_event_subscribe_struct;
25
+ VALUE v_sctp_receive_info_struct;
24
26
 
25
27
  #if !defined(IOV_MAX)
26
28
  #if defined(_SC_IOV_MAX)
27
29
  #define IOV_MAX (sysconf(_SC_IOV_MAX))
28
30
  #else
29
- // Assume infinity, or let the syscall return with error
30
31
  #define IOV_MAX INT_MAX
31
32
  #endif
32
33
  #endif
33
34
 
34
- #define ARY2IOVEC(iov,ary) \
35
- do { \
36
- VALUE *cur; \
37
- struct iovec *tmp; \
38
- long n; \
39
- cur = RARRAY_PTR(ary); \
40
- n = RARRAY_LEN(ary); \
41
- iov = tmp = alloca(sizeof(struct iovec) * n); \
42
- for (; --n >= 0; tmp++, cur++) { \
43
- if (TYPE(*cur) != T_STRING) \
44
- rb_raise(rb_eArgError, "must be an array of strings"); \
45
- tmp->iov_base = RSTRING_PTR(*cur); \
46
- tmp->iov_len = RSTRING_LEN(*cur); \
47
- } \
48
- } while (0)
49
-
50
35
  // TODO: Yes, I know I need to update the signature.
51
36
  VALUE convert_sockaddr_in_to_struct(struct sockaddr_in* addr){
52
37
  char ipbuf[INET6_ADDRSTRLEN];
@@ -94,7 +79,7 @@ VALUE rb_hash_aref2(VALUE v_hash, const char* key){
94
79
  * socket2 = SCTP::Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
95
80
  */
96
81
  static VALUE rsctp_init(int argc, VALUE* argv, VALUE self){
97
- int sock_fd;
82
+ int fileno;
98
83
  VALUE v_domain, v_type;
99
84
 
100
85
  rb_scan_args(argc, argv, "02", &v_domain, &v_type);
@@ -105,14 +90,14 @@ static VALUE rsctp_init(int argc, VALUE* argv, VALUE self){
105
90
  if(NIL_P(v_type))
106
91
  v_type = INT2NUM(SOCK_SEQPACKET);
107
92
 
108
- sock_fd = socket(NUM2INT(v_domain), NUM2INT(v_type), IPPROTO_SCTP);
93
+ fileno = socket(NUM2INT(v_domain), NUM2INT(v_type), IPPROTO_SCTP);
109
94
 
110
- if(sock_fd < 0)
95
+ if(fileno < 0)
111
96
  rb_raise(rb_eSystemCallError, "socket: %s", strerror(errno));
112
97
 
113
98
  rb_iv_set(self, "@domain", v_domain);
114
99
  rb_iv_set(self, "@type", v_type);
115
- rb_iv_set(self, "@sock_fd", INT2NUM(sock_fd));
100
+ rb_iv_set(self, "@fileno", INT2NUM(fileno));
116
101
  rb_iv_set(self, "@association_id", INT2NUM(0));
117
102
 
118
103
  return self;
@@ -131,19 +116,19 @@ static VALUE rsctp_init(int argc, VALUE* argv, VALUE self){
131
116
  * socket = SCTP::Socket.new
132
117
  *
133
118
  * # Bind 2 addresses
134
- * socket.bind(:port => 64325, :addresses => ['10.0.4.5', '10.0.5.5'])
119
+ * socket.bindx(:port => 64325, :addresses => ['10.0.4.5', '10.0.5.5'])
135
120
  *
136
121
  * # Remove 1 later
137
- * socket.bind(:addresses => ['10.0.4.5'], :flags => SCTP::Socket::BINDX_REM_ADDR)
122
+ * socket.bindx(:addresses => ['10.0.4.5'], :flags => SCTP::Socket::BINDX_REM_ADDR)
138
123
  *
139
124
  * If no addresses are specified, then it will bind to all available interfaces. If
140
125
  * no port is specified, then one will be assigned by the host.
141
126
  *
142
127
  * Returns the port that it was bound to.
143
128
  */
144
- static VALUE rsctp_bind(int argc, VALUE* argv, VALUE self){
129
+ static VALUE rsctp_bindx(int argc, VALUE* argv, VALUE self){
145
130
  struct sockaddr_in addrs[8];
146
- int i, sock_fd, num_ip, flags, domain, port;
131
+ int i, fileno, num_ip, flags, domain, port;
147
132
  VALUE v_addresses, v_port, v_flags, v_address, v_options;
148
133
 
149
134
  rb_scan_args(argc, argv, "01", &v_options);
@@ -173,7 +158,7 @@ static VALUE rsctp_bind(int argc, VALUE* argv, VALUE self){
173
158
  num_ip = RARRAY_LEN(v_addresses);
174
159
 
175
160
  domain = NUM2INT(rb_iv_get(self, "@domain"));
176
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
161
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
177
162
 
178
163
  if(num_ip > 1){
179
164
  for(i = 0; i < num_ip; i++){
@@ -189,19 +174,21 @@ static VALUE rsctp_bind(int argc, VALUE* argv, VALUE self){
189
174
  addrs[0].sin_addr.s_addr = htonl(INADDR_ANY);
190
175
  }
191
176
 
192
- if(sctp_bindx(sock_fd, (struct sockaddr *) addrs, num_ip, flags) != 0)
177
+ if(sctp_bindx(fileno, (struct sockaddr *) addrs, num_ip, flags) != 0)
193
178
  rb_raise(rb_eSystemCallError, "sctp_bindx: %s", strerror(errno));
194
179
 
195
180
  if(port == 0){
196
181
  struct sockaddr_in sin;
197
182
  socklen_t len = sizeof(sin);
198
183
 
199
- if(getsockname(sock_fd, (struct sockaddr *)&sin, &len) == -1)
184
+ if(getsockname(fileno, (struct sockaddr *)&sin, &len) == -1)
200
185
  rb_raise(rb_eSystemCallError, "getsockname: %s", strerror(errno));
201
186
 
202
187
  port = sin.sin_port;
203
188
  }
204
189
 
190
+ rb_iv_set(self, "@port", INT2NUM(port));
191
+
205
192
  return INT2NUM(port);
206
193
  }
207
194
 
@@ -212,13 +199,13 @@ static VALUE rsctp_bind(int argc, VALUE* argv, VALUE self){
212
199
  * Example:
213
200
  *
214
201
  * socket = SCTP::Socket.new
215
- * socket.connect(:port => 62354, :addresses => ['10.0.4.5', '10.0.5.5'])
202
+ * socket.connectx(:port => 62354, :addresses => ['10.0.4.5', '10.0.5.5'])
216
203
  *
217
204
  * Note that this will also set/update the object's association_id.
218
205
  */
219
- static VALUE rsctp_connect(int argc, VALUE* argv, VALUE self){
206
+ static VALUE rsctp_connectx(int argc, VALUE* argv, VALUE self){
220
207
  struct sockaddr_in addrs[8];
221
- int i, num_ip, sock_fd;
208
+ int i, num_ip, fileno;
222
209
  sctp_assoc_t assoc;
223
210
  VALUE v_address, v_domain, v_options, v_addresses, v_port;
224
211
 
@@ -250,9 +237,9 @@ static VALUE rsctp_connect(int argc, VALUE* argv, VALUE self){
250
237
  addrs[i].sin_addr.s_addr = inet_addr(StringValueCStr(v_address));
251
238
  }
252
239
 
253
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
240
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
254
241
 
255
- if(sctp_connectx(sock_fd, (struct sockaddr *) addrs, num_ip, &assoc) < 0)
242
+ if(sctp_connectx(fileno, (struct sockaddr *) addrs, num_ip, &assoc) < 0)
256
243
  rb_raise(rb_eSystemCallError, "sctp_connectx: %s", strerror(errno));
257
244
 
258
245
  rb_iv_set(self, "@association_id", INT2NUM(assoc));
@@ -269,30 +256,55 @@ static VALUE rsctp_connect(int argc, VALUE* argv, VALUE self){
269
256
  * socket.close
270
257
  */
271
258
  static VALUE rsctp_close(VALUE self){
272
- VALUE v_sock_fd = rb_iv_get(self, "@sock_fd");
259
+ VALUE v_fileno = rb_iv_get(self, "@fileno");
273
260
 
274
- if(close(NUM2INT(v_sock_fd)))
261
+ if(close(NUM2INT(v_fileno)))
275
262
  rb_raise(rb_eSystemCallError, "close: %s", strerror(errno));
276
263
 
277
264
  return self;
278
265
  }
279
266
 
280
267
  /*
281
- * Return an array of all addresses of a peer.
268
+ * Return an array of all addresses of a peer of the current socket
269
+ * and association number.
270
+ *
271
+ * You may optionally pass a assocation fileno and association ID. Typically
272
+ * this information would come from the peeloff method.
273
+ *
274
+ * Example:
275
+ *
276
+ * socket = SCTP::Socket.new
277
+ * # ...
278
+ * p socket.getpeernames
279
+ *
280
+ * info = socket.recvmsg
281
+ * association_fileno = socket.peeloff(info.association_id)
282
+ *
283
+ * p socket.getpeernames(association_fileno, info.association_id)
282
284
  */
283
- static VALUE rsctp_getpeernames(VALUE self){
285
+ static VALUE rsctp_getpeernames(int argc, VALUE* argv, VALUE self){
284
286
  sctp_assoc_t assoc_id;
285
287
  struct sockaddr* addrs;
286
- int i, sock_fd, num_addrs;
288
+ int i, fileno, num_addrs;
287
289
  char str[16];
290
+ VALUE v_fileno, v_association_id;
288
291
  VALUE v_array = rb_ary_new();
289
292
 
290
293
  bzero(&addrs, sizeof(addrs));
291
294
 
292
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
293
- assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
295
+ rb_scan_args(argc, argv, "02", &v_fileno, &v_association_id);
296
+
297
+ if(NIL_P(v_fileno))
298
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
299
+ else
300
+ fileno = NUM2INT(v_fileno);
294
301
 
295
- num_addrs = sctp_getpaddrs(sock_fd, assoc_id, &addrs);
302
+ if(NIL_P(v_association_id))
303
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
304
+ else
305
+ assoc_id = NUM2INT(v_association_id);
306
+
307
+ num_addrs = sctp_getpaddrs(fileno, assoc_id, &addrs);
296
308
 
297
309
  if(num_addrs < 0){
298
310
  sctp_freepaddrs(addrs);
@@ -318,20 +330,35 @@ static VALUE rsctp_getpeernames(VALUE self){
318
330
  * socket = SCTP::Socket.new
319
331
  * socket.bind(:addresses => ['10.0.4.5', '10.0.5.5'])
320
332
  * socket.getlocalnames # => ['10.0.4.5', '10.0.5.5'])
333
+ *
334
+ * # or get info from a peeled off association...
335
+ *
336
+ * assoc_fileno = socket.peeloff(some_association_id)
337
+ * socket.getlocalnames(assoc_fileno, some_association_id)
321
338
  */
322
- static VALUE rsctp_getlocalnames(VALUE self){
339
+ static VALUE rsctp_getlocalnames(int argc, VALUE* argv, VALUE self){
323
340
  sctp_assoc_t assoc_id;
324
341
  struct sockaddr* addrs;
325
- int i, sock_fd, num_addrs;
342
+ int i, fileno, num_addrs;
326
343
  char str[16];
344
+ VALUE v_assoc_fileno, v_assoc_id;
327
345
  VALUE v_array = rb_ary_new();
328
346
 
329
347
  bzero(&addrs, sizeof(addrs));
330
348
 
331
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
332
- assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
349
+ rb_scan_args(argc, argv, "02", &v_assoc_fileno, &v_assoc_id);
333
350
 
334
- num_addrs = sctp_getladdrs(sock_fd, assoc_id, &addrs);
351
+ if(NIL_P(v_assoc_fileno))
352
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
353
+ else
354
+ fileno = NUM2INT(v_assoc_fileno);
355
+
356
+ if(NIL_P(v_assoc_id))
357
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
358
+ else
359
+ assoc_id = NUM2INT(v_assoc_id);
360
+
361
+ num_addrs = sctp_getladdrs(fileno, assoc_id, &addrs);
335
362
 
336
363
  if(num_addrs < 0){
337
364
  sctp_freeladdrs(addrs);
@@ -350,19 +377,58 @@ static VALUE rsctp_getlocalnames(VALUE self){
350
377
  }
351
378
 
352
379
  #ifdef HAVE_SCTP_SENDV
353
- static VALUE rsctp_sendv(VALUE self, VALUE v_messages){
354
- struct iovec* iov;
355
- struct sockaddr* addrs[8];
380
+ /*
381
+ * Transmit a message to an SCTP endpoint using a gather-write. The following
382
+ * hash of options is permitted:
383
+ *
384
+ * * message - An array of strings that will be joined into a single message.
385
+ * * addresses - An array of IP addresses to setup an association to send the message.
386
+ * * info_type - The type of information provided. The default is SCTP_SENDV_SNDINFO.
387
+ *
388
+ * Example:
389
+ *
390
+ * socket = SCTP::Socket.new
391
+ *
392
+ * socket.sendv
393
+ * :message => ['Hello ', 'World.'],
394
+ * :addresses => ['10.0.5.4', '10.0.6.4'],
395
+ * :info_type => SCTP::Socket:::SCTP_SENDV_SNDINFO
396
+ * )
397
+ *
398
+ * CAVEAT: Currently addresses does not work, and info_type is not yet supported.
399
+ *
400
+ * Returns the number of bytes sent.
401
+ */
402
+ static VALUE rsctp_sendv(VALUE self, VALUE v_options){
403
+ VALUE v_msg, v_message, v_addresses;
404
+ struct iovec iov[IOV_MAX];
405
+ struct sockaddr_in* addrs;
356
406
  struct sctp_sndinfo info;
357
- int sock_fd, num_bytes, size;
407
+ int i, fileno, num_bytes, size, num_ip;
358
408
 
359
- Check_Type(v_messages, T_ARRAY);
360
- bzero(&addrs, sizeof(addrs));
409
+ Check_Type(v_options, T_HASH);
410
+
411
+ bzero(&iov, sizeof(iov));
412
+ bzero(&info, sizeof(info));
413
+
414
+ v_message = rb_hash_aref2(v_options, "message");
415
+ v_addresses = rb_hash_aref2(v_options, "addresses");
361
416
 
362
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
417
+ if(!NIL_P(v_message))
418
+ Check_Type(v_message, T_ARRAY);
419
+
420
+ if(!NIL_P(v_addresses)){
421
+ Check_Type(v_addresses, T_ARRAY);
422
+ num_ip = RARRAY_LEN(v_addresses);
423
+ addrs = (struct sockaddr_in*)alloca(sizeof(struct sockaddr_in) * num_ip);
424
+ }
425
+ else{
426
+ addrs = NULL;
427
+ num_ip = 0;
428
+ }
363
429
 
364
- Check_Type(v_messages, T_ARRAY);
365
- size = RARRAY_LEN(v_messages);
430
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
431
+ size = RARRAY_LEN(v_message);
366
432
 
367
433
  if(!size)
368
434
  rb_raise(rb_eArgError, "Must contain at least one message");
@@ -370,17 +436,41 @@ static VALUE rsctp_sendv(VALUE self, VALUE v_messages){
370
436
  if(size > IOV_MAX)
371
437
  rb_raise(rb_eArgError, "Array size is greater than IOV_MAX");
372
438
 
373
- ARY2IOVEC(iov, v_messages);
374
-
375
439
  info.snd_flags = SCTP_UNORDERED;
376
440
  info.snd_assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
377
441
 
442
+ if(!NIL_P(v_addresses)){
443
+ int i, port;
444
+ VALUE v_address, v_port;
445
+
446
+ v_port = NUM2INT(rb_iv_get(self, "@port"));
447
+
448
+ if(NIL_P(v_port))
449
+ port = 0;
450
+ else
451
+ port = NUM2INT(v_port);
452
+
453
+ for(i = 0; i < num_ip; i++){
454
+ v_address = RARRAY_PTR(v_addresses)[i];
455
+ addrs->sin_family = NUM2INT(rb_iv_get(self, "@domain"));
456
+ addrs->sin_port = htons(port);
457
+ addrs->sin_addr.s_addr = inet_addr(StringValueCStr(v_address));
458
+ addrs += sizeof(struct sockaddr_in);
459
+ }
460
+ }
461
+
462
+ for(i = 0; i < size; i++){
463
+ v_msg = RARRAY_PTR(v_message)[i];
464
+ iov[i].iov_base = StringValueCStr(v_msg);
465
+ iov[i].iov_len = RSTRING_LEN(v_msg);
466
+ }
467
+
378
468
  num_bytes = sctp_sendv(
379
- sock_fd,
469
+ fileno,
380
470
  iov,
381
471
  size,
382
- NULL,
383
- 0,
472
+ (struct sockaddr*)addrs,
473
+ num_ip,
384
474
  &info,
385
475
  sizeof(info),
386
476
  SCTP_SENDV_SNDINFO,
@@ -394,6 +484,74 @@ static VALUE rsctp_sendv(VALUE self, VALUE v_messages){
394
484
  }
395
485
  #endif
396
486
 
487
+ #ifdef HAVE_SCTP_RECVV
488
+ static VALUE rsctp_recvv(int argc, VALUE* argv, VALUE self){
489
+ VALUE v_flags;
490
+ int fileno, flags, bytes, on;
491
+ uint infotype;
492
+ socklen_t infolen;
493
+ struct iovec iov[1];
494
+ struct sctp_rcvinfo info;
495
+ char buffer[1024];
496
+
497
+ bzero(&iov, sizeof(iov));
498
+ bzero(&info, sizeof(info));
499
+ bzero(&buffer, sizeof(buffer));
500
+
501
+ iov->iov_base = buffer;
502
+ iov->iov_len = sizeof(buffer);
503
+
504
+ rb_scan_args(argc, argv, "01", &v_flags);
505
+
506
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
507
+
508
+ if(NIL_P(v_flags))
509
+ flags = 0;
510
+ else
511
+ flags = NUM2INT(v_flags);
512
+
513
+ on = 1;
514
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(on)) < 0)
515
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
516
+
517
+ infolen = sizeof(struct sctp_rcvinfo);
518
+ infotype = 0;
519
+
520
+ bytes = sctp_recvv(
521
+ fileno,
522
+ iov,
523
+ 1,
524
+ NULL,
525
+ NULL,
526
+ &info,
527
+ &infolen,
528
+ &infotype,
529
+ &flags
530
+ );
531
+
532
+ if(bytes < 0)
533
+ rb_raise(rb_eSystemCallError, "sctp_recvv: %s", strerror(errno));
534
+
535
+ if(infotype != SCTP_RECVV_RCVINFO){
536
+ return Qnil;
537
+ }
538
+ else{
539
+ return rb_struct_new(
540
+ v_sctp_receive_info_struct,
541
+ rb_str_new2(iov->iov_base),
542
+ UINT2NUM(info.rcv_sid),
543
+ UINT2NUM(info.rcv_ssn),
544
+ UINT2NUM(info.rcv_flags),
545
+ UINT2NUM(info.rcv_ppid),
546
+ UINT2NUM(info.rcv_tsn),
547
+ UINT2NUM(info.rcv_cumtsn),
548
+ UINT2NUM(info.rcv_context),
549
+ UINT2NUM(info.rcv_assoc_id)
550
+ );
551
+ }
552
+ }
553
+ #endif
554
+
397
555
  /*
398
556
  * Send a message on an already-connected socket to a specific association.
399
557
  *
@@ -410,7 +568,7 @@ static VALUE rsctp_send(VALUE self, VALUE v_options){
410
568
  uint16_t stream;
411
569
  uint32_t ppid, send_flags, ctrl_flags, ttl, context;
412
570
  ssize_t num_bytes;
413
- int sock_fd;
571
+ int fileno;
414
572
  sctp_assoc_t assoc_id;
415
573
  struct sctp_sndrcvinfo info;
416
574
  VALUE v_msg, v_stream, v_ppid, v_context, v_send_flags, v_ctrl_flags, v_ttl, v_assoc_id;
@@ -471,10 +629,10 @@ static VALUE rsctp_send(VALUE self, VALUE v_options){
471
629
  info.sinfo_timetolive = ttl;
472
630
  info.sinfo_assoc_id = assoc_id;
473
631
 
474
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
632
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
475
633
 
476
634
  num_bytes = sctp_send(
477
- sock_fd,
635
+ fileno,
478
636
  StringValueCStr(v_msg),
479
637
  RSTRING_LEN(v_msg),
480
638
  &info,
@@ -491,14 +649,14 @@ static VALUE rsctp_send(VALUE self, VALUE v_options){
491
649
  * Transmit a message to an SCTP endpoint. The following hash of options
492
650
  * is permitted:
493
651
  *
494
- * :message -> The message to send to the endpoint. Mandatory.
495
- * :stream -> The SCTP stream number you wish to send the message on.
496
- * :to -> An array of addresses to send the message to.
497
- * :context -> The default context used for the sendmsg call if the send fails.
498
- * :ppid -> The payload protocol identifier that is passed to the peer endpoint.
499
- * :flags -> A bitwise integer that contain one or more values that control behavior.
652
+ * :message -> The message to send to the endpoint. Mandatory.
653
+ * :stream -> The SCTP stream number you wish to send the message on.
654
+ * :addresses -> An array of addresses to send the message to.
655
+ * :context -> The default context used for the sendmsg call if the send fails.
656
+ * :ppid -> The payload protocol identifier that is passed to the peer endpoint.
657
+ * :flags -> A bitwise integer that contain one or more values that control behavior.
500
658
  *
501
- * Note that the :to option is not mandatory in a one-to-one (SOCK_STREAM)
659
+ * Note that the :addresses option is not mandatory in a one-to-one (SOCK_STREAM)
502
660
  * socket connection. However, it must have been set previously via the
503
661
  * connect method.
504
662
  *
@@ -507,12 +665,14 @@ static VALUE rsctp_send(VALUE self, VALUE v_options){
507
665
  * socket = SCTP::Socket.new
508
666
  *
509
667
  * socket.sendmsg(
510
- * :message => "Hello World!",
511
- * :stream => 3,
512
- * :flags => SCTP::Socket::SCTP_UNORDERED | SCTP::Socket::SCTP_SENDALL,
513
- * :ttl => 100,
514
- * :to => ['10.0.5.4', '10.0.6.4']
668
+ * :message => "Hello World!",
669
+ * :stream => 3,
670
+ * :flags => SCTP::Socket::SCTP_UNORDERED | SCTP::Socket::SCTP_SENDALL,
671
+ * :ttl => 100,
672
+ * :addresses => ['10.0.5.4', '10.0.6.4']
515
673
  * )
674
+ *
675
+ * Returns the number of bytes sent.
516
676
  */
517
677
  static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
518
678
  VALUE v_msg, v_ppid, v_flags, v_stream, v_ttl, v_context, v_addresses;
@@ -520,7 +680,7 @@ static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
520
680
  uint32_t ppid, flags, ttl, context;
521
681
  ssize_t num_bytes;
522
682
  struct sockaddr_in addrs[8];
523
- int sock_fd, size;
683
+ int fileno, size;
524
684
 
525
685
  Check_Type(v_options, T_HASH);
526
686
 
@@ -587,10 +747,10 @@ static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
587
747
  size = 0;
588
748
  }
589
749
 
590
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
750
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
591
751
 
592
752
  num_bytes = sctp_sendmsg(
593
- sock_fd,
753
+ fileno,
594
754
  StringValueCStr(v_msg),
595
755
  RSTRING_LEN(v_msg),
596
756
  (struct sockaddr*)addrs,
@@ -631,7 +791,7 @@ static VALUE rsctp_recvmsg(int argc, VALUE* argv, VALUE self){
631
791
  VALUE v_flags, v_notification, v_message;
632
792
  struct sctp_sndrcvinfo sndrcvinfo;
633
793
  struct sockaddr_in clientaddr;
634
- int flags, bytes, sock_fd;
794
+ int flags, bytes, fileno;
635
795
  char buffer[1024]; // TODO: Let this be configurable?
636
796
  socklen_t length;
637
797
 
@@ -642,12 +802,15 @@ static VALUE rsctp_recvmsg(int argc, VALUE* argv, VALUE self){
642
802
  else
643
803
  flags = NUM2INT(v_flags);
644
804
 
645
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
805
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
646
806
  length = sizeof(struct sockaddr_in);
807
+
647
808
  bzero(buffer, sizeof(buffer));
809
+ bzero(&clientaddr, sizeof(clientaddr));
810
+ bzero(&sndrcvinfo, sizeof(sndrcvinfo));
648
811
 
649
812
  bytes = sctp_recvmsg(
650
- sock_fd,
813
+ fileno,
651
814
  buffer,
652
815
  sizeof(buffer),
653
816
  (struct sockaddr*)&clientaddr,
@@ -870,7 +1033,7 @@ static VALUE rsctp_recvmsg(int argc, VALUE* argv, VALUE self){
870
1033
  * By default these values are set to zero (i.e. ignored).
871
1034
  */
872
1035
  static VALUE rsctp_set_initmsg(VALUE self, VALUE v_options){
873
- int sock_fd;
1036
+ int fileno;
874
1037
  struct sctp_initmsg initmsg;
875
1038
  VALUE v_output, v_input, v_attempts, v_timeout;
876
1039
 
@@ -881,7 +1044,7 @@ static VALUE rsctp_set_initmsg(VALUE self, VALUE v_options){
881
1044
  v_attempts = rb_hash_aref2(v_options, "max_attempts");
882
1045
  v_timeout = rb_hash_aref2(v_options, "timeout");
883
1046
 
884
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
1047
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
885
1048
 
886
1049
  if(!NIL_P(v_output))
887
1050
  initmsg.sinit_num_ostreams = NUM2INT(v_output);
@@ -895,15 +1058,15 @@ static VALUE rsctp_set_initmsg(VALUE self, VALUE v_options){
895
1058
  if(!NIL_P(v_timeout))
896
1059
  initmsg.sinit_max_init_timeo = NUM2INT(v_timeout);
897
1060
 
898
- if(setsockopt(sock_fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg)) < 0)
1061
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg)) < 0)
899
1062
  rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
900
1063
 
901
1064
  return self;
902
1065
  }
903
1066
 
904
1067
  /*
905
- * Subscribe to various notification types, which will generate additional
906
- * data that the socket may receive. The possible notification types are
1068
+ * Subscribe to various notification type events, which will generate additional
1069
+ * data that the socket may receive. The possible notification type events are
907
1070
  * as follows:
908
1071
  *
909
1072
  * :association
@@ -919,7 +1082,7 @@ static VALUE rsctp_set_initmsg(VALUE self, VALUE v_options){
919
1082
  * - The peer has sent a shutdown to the local endpoint.
920
1083
  *
921
1084
  * :data_io
922
- * - Message data was received. On by default.
1085
+ * - Message data was received. You will want to subscribe to this in most cases.
923
1086
  *
924
1087
  * Others:
925
1088
  *
@@ -932,21 +1095,21 @@ static VALUE rsctp_set_initmsg(VALUE self, VALUE v_options){
932
1095
  * :sender_dry
933
1096
  * :peer_error
934
1097
  *
935
- * By default only data_io is subscribed to.
936
- *
937
1098
  * Example:
938
1099
  *
939
1100
  * socket = SCTP::Socket.new
940
1101
  *
941
1102
  * socket.bind(:port => port, :addresses => ['127.0.0.1'])
942
- * socket.subscribe(:shutdown => true, :send_failure => true)
1103
+ * socket.subscribe(:data_io => true, :shutdown => true, :send_failure => true)
943
1104
  */
944
1105
  static VALUE rsctp_subscribe(VALUE self, VALUE v_options){
945
- int sock_fd;
1106
+ int fileno;
946
1107
  struct sctp_event_subscribe events;
947
1108
 
948
1109
  bzero(&events, sizeof(events));
949
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
1110
+ Check_Type(v_options, T_HASH);
1111
+
1112
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
950
1113
 
951
1114
  if(RTEST(rb_hash_aref2(v_options, "data_io")))
952
1115
  events.sctp_data_io_event = 1;
@@ -982,7 +1145,7 @@ static VALUE rsctp_subscribe(VALUE self, VALUE v_options){
982
1145
  if(RTEST(rb_hash_aref2(v_options, "sender_dry")))
983
1146
  events.sctp_sender_dry_event = 1;
984
1147
 
985
- if(setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events)) < 0)
1148
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events)) < 0)
986
1149
  rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
987
1150
 
988
1151
  return self;
@@ -993,68 +1156,87 @@ static VALUE rsctp_subscribe(VALUE self, VALUE v_options){
993
1156
  * will be used to accept incoming connection requests.
994
1157
  *
995
1158
  * The backlog argument defines the maximum length to which the queue of
996
- * pending connections for sockfd may grow. The default is 1024.
1159
+ * pending connections for sockfd may grow. The default value is 128. The
1160
+ * maximum value is Socket::SOMAXCONN.
1161
+ *
1162
+ * Why a default of 128? The answer is a "best guess" compromise between
1163
+ * handling server load versus avoiding SYN flood attacks. I leave it as an
1164
+ * exercise to the programmer to adjust as desired.
1165
+ *
1166
+ * See https://tangentsoft.com/wskfaq/advanced.html#backlog if you want
1167
+ * more details on the advantages and drawbacks of various values.
997
1168
  *
998
1169
  * Example:
999
1170
  *
1000
1171
  * socket = SCTP::Socket.new
1001
1172
  * socket.bind(:port => 62534, :addresses => ['127.0.0.1'])
1002
1173
  * socket.listen
1003
- *
1004
1174
  */
1005
1175
  static VALUE rsctp_listen(int argc, VALUE* argv, VALUE self){
1006
1176
  VALUE v_backlog;
1007
- int backlog, sock_fd;
1177
+ int backlog, fileno;
1008
1178
 
1009
1179
  rb_scan_args(argc, argv, "01", &v_backlog);
1010
1180
 
1011
1181
  if(NIL_P(v_backlog))
1012
- backlog = 1024;
1182
+ backlog = 128;
1013
1183
  else
1014
1184
  backlog = NUM2INT(v_backlog);
1015
1185
 
1016
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
1186
+ if(backlog > SOMAXCONN)
1187
+ rb_raise(rb_eArgError, "backlog value exceeds maximum value of: %i", SOMAXCONN);
1017
1188
 
1018
- if(listen(sock_fd, backlog) < 0)
1019
- rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
1189
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1190
+
1191
+ if(listen(fileno, backlog) < 0)
1192
+ rb_raise(rb_eSystemCallError, "listen: %s", strerror(errno));
1020
1193
 
1021
1194
  return self;
1022
1195
  }
1023
1196
 
1024
1197
  /*
1025
1198
  * Extracts an association contained by a one-to-many socket connection into
1026
- * a one-to-one style socket. Note that this modifies the underlying sock_fd.
1199
+ * a one-to-one style socket. Returns the socket descriptor (fileno).
1200
+ *
1201
+ * Example:
1202
+ *
1203
+ * socket = SCTP::Socket.new
1204
+ * # etc...
1205
+ *
1206
+ * while true
1207
+ * info = socket.recvmsg
1208
+ * assoc_fileno = socket.peeloff(info.association_id)
1209
+ * # ... Do something with this new
1210
+ * end
1027
1211
  */
1028
1212
  static VALUE rsctp_peeloff(VALUE self, VALUE v_assoc_id){
1029
- int sock_fd, new_sock_fd;
1213
+ int fileno, assoc_fileno;
1030
1214
  sctp_assoc_t assoc_id;
1031
1215
 
1032
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
1216
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1033
1217
  assoc_id = NUM2INT(v_assoc_id);
1034
1218
 
1035
- new_sock_fd = sctp_peeloff(sock_fd, assoc_id);
1219
+ assoc_fileno = sctp_peeloff(fileno, assoc_id);
1036
1220
 
1037
- if(new_sock_fd < 0)
1221
+ if(assoc_fileno < 0)
1038
1222
  rb_raise(rb_eSystemCallError, "sctp_peeloff: %s", strerror(errno));
1039
1223
 
1040
- rb_iv_set(self, "@sock_fd", INT2NUM(new_sock_fd));
1041
-
1042
- return self;
1224
+ return INT2NUM(assoc_fileno);
1043
1225
  }
1044
1226
 
1045
1227
  static VALUE rsctp_get_default_send_params(VALUE self){
1046
- int sock_fd;
1228
+ int fileno;
1047
1229
  socklen_t size;
1048
1230
  sctp_assoc_t assoc_id;
1049
1231
  struct sctp_sndrcvinfo sndrcv;
1050
1232
 
1051
1233
  bzero(&sndrcv, sizeof(sndrcv));
1052
1234
 
1053
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
1235
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1054
1236
  assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
1055
1237
  size = sizeof(struct sctp_sndrcvinfo);
1056
1238
 
1057
- if(sctp_opt_info(sock_fd, assoc_id, SCTP_DEFAULT_SEND_PARAM, (void*)&sndrcv, &size) < 0)
1239
+ if(sctp_opt_info(fileno, assoc_id, SCTP_DEFAULT_SEND_PARAM, (void*)&sndrcv, &size) < 0)
1058
1240
  rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
1059
1241
 
1060
1242
  return rb_struct_new(
@@ -1072,18 +1254,18 @@ static VALUE rsctp_get_default_send_params(VALUE self){
1072
1254
  }
1073
1255
 
1074
1256
  static VALUE rsctp_get_association_info(VALUE self){
1075
- int sock_fd;
1257
+ int fileno;
1076
1258
  socklen_t size;
1077
1259
  sctp_assoc_t assoc_id;
1078
1260
  struct sctp_assocparams assoc;
1079
1261
 
1080
1262
  bzero(&assoc, sizeof(assoc));
1081
1263
 
1082
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
1264
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1083
1265
  assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
1084
1266
  size = sizeof(struct sctp_assocparams);
1085
1267
 
1086
- if(sctp_opt_info(sock_fd, assoc_id, SCTP_ASSOCINFO, (void*)&assoc, &size) < 0)
1268
+ if(sctp_opt_info(fileno, assoc_id, SCTP_ASSOCINFO, (void*)&assoc, &size) < 0)
1087
1269
  rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
1088
1270
 
1089
1271
  return rb_struct_new(
@@ -1098,10 +1280,10 @@ static VALUE rsctp_get_association_info(VALUE self){
1098
1280
  }
1099
1281
 
1100
1282
  static VALUE rsctp_shutdown(int argc, VALUE* argv, VALUE self){
1101
- int how, sock_fd;
1283
+ int how, fileno;
1102
1284
  VALUE v_how;
1103
1285
 
1104
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
1286
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1105
1287
 
1106
1288
  rb_scan_args(argc, argv, "01", &v_how);
1107
1289
 
@@ -1113,25 +1295,25 @@ static VALUE rsctp_shutdown(int argc, VALUE* argv, VALUE self){
1113
1295
  how = NUM2INT(v_how);
1114
1296
  }
1115
1297
 
1116
- if(shutdown(sock_fd, how) < 0)
1298
+ if(shutdown(fileno, how) < 0)
1117
1299
  rb_raise(rb_eSystemCallError, "shutdown: %s", strerror(errno));
1118
1300
 
1119
1301
  return self;
1120
1302
  }
1121
1303
 
1122
1304
  static VALUE rsctp_get_retransmission_info(VALUE self){
1123
- int sock_fd;
1305
+ int fileno;
1124
1306
  socklen_t size;
1125
1307
  sctp_assoc_t assoc_id;
1126
1308
  struct sctp_rtoinfo rto;
1127
1309
 
1128
1310
  bzero(&rto, sizeof(rto));
1129
1311
 
1130
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
1312
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1131
1313
  assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
1132
1314
  size = sizeof(struct sctp_rtoinfo);
1133
1315
 
1134
- if(sctp_opt_info(sock_fd, assoc_id, SCTP_RTOINFO, (void*)&rto, &size) < 0)
1316
+ if(sctp_opt_info(fileno, assoc_id, SCTP_RTOINFO, (void*)&rto, &size) < 0)
1135
1317
  rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
1136
1318
 
1137
1319
  return rb_struct_new(
@@ -1143,8 +1325,29 @@ static VALUE rsctp_get_retransmission_info(VALUE self){
1143
1325
  );
1144
1326
  }
1145
1327
 
1328
+ /*
1329
+ * Get the status of a connected socket.
1330
+ *
1331
+ * Example:
1332
+ *
1333
+ * socket = SCTP::Socket.new
1334
+ * socket.connect(...)
1335
+ * socket.get_status
1336
+ *
1337
+ * Returns a Struct::Status object, which contains the following fields:
1338
+ *
1339
+ * * association_id
1340
+ * * state
1341
+ * * receive_window
1342
+ * * unacknowledged_data
1343
+ * * pending_data
1344
+ * * inbound_streams
1345
+ * * outbound_streams
1346
+ * * fragmentation_point
1347
+ * * primary (IP)
1348
+ */
1146
1349
  static VALUE rsctp_get_status(VALUE self){
1147
- int sock_fd;
1350
+ int fileno;
1148
1351
  socklen_t size;
1149
1352
  sctp_assoc_t assoc_id;
1150
1353
  struct sctp_status status;
@@ -1153,11 +1356,11 @@ static VALUE rsctp_get_status(VALUE self){
1153
1356
 
1154
1357
  bzero(&status, sizeof(status));
1155
1358
 
1156
- sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
1359
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1157
1360
  assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
1158
1361
  size = sizeof(struct sctp_status);
1159
1362
 
1160
- if(sctp_opt_info(sock_fd, assoc_id, SCTP_STATUS, (void*)&status, &size) < 0)
1363
+ if(sctp_opt_info(fileno, assoc_id, SCTP_STATUS, (void*)&status, &size) < 0)
1161
1364
  rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
1162
1365
 
1163
1366
  spinfo = &status.sstat_primary;
@@ -1186,7 +1389,40 @@ static VALUE rsctp_get_status(VALUE self){
1186
1389
  );
1187
1390
  }
1188
1391
 
1189
- void Init_socket(){
1392
+ static VALUE rsctp_get_subscriptions(VALUE self){
1393
+ int fileno;
1394
+ socklen_t size;
1395
+ sctp_assoc_t assoc_id;
1396
+ struct sctp_event_subscribe events;
1397
+
1398
+ bzero(&events, sizeof(events));
1399
+
1400
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1401
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
1402
+ size = sizeof(struct sctp_event_subscribe);
1403
+
1404
+ if(sctp_opt_info(fileno, assoc_id, SCTP_EVENTS, (void*)&events, &size) < 0)
1405
+ rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
1406
+
1407
+ return rb_struct_new(v_sctp_event_subscribe_struct,
1408
+ (events.sctp_data_io_event ? Qtrue : Qfalse),
1409
+ (events.sctp_association_event ? Qtrue : Qfalse),
1410
+ (events.sctp_address_event ? Qtrue : Qfalse),
1411
+ (events.sctp_send_failure_event ? Qtrue : Qfalse),
1412
+ (events.sctp_peer_error_event ? Qtrue : Qfalse),
1413
+ (events.sctp_shutdown_event ? Qtrue : Qfalse),
1414
+ (events.sctp_partial_delivery_event ? Qtrue : Qfalse),
1415
+ (events.sctp_adaptation_layer_event ? Qtrue : Qfalse),
1416
+ (events.sctp_authentication_event ? Qtrue : Qfalse),
1417
+ (events.sctp_sender_dry_event ? Qtrue : Qfalse),
1418
+ (events.sctp_stream_reset_event ? Qtrue : Qfalse),
1419
+ (events.sctp_assoc_reset_event ? Qtrue : Qfalse),
1420
+ (events.sctp_stream_change_event ? Qtrue : Qfalse),
1421
+ (events.sctp_send_failure_event_event ? Qtrue : Qfalse)
1422
+ );
1423
+ }
1424
+
1425
+ void Init_socket(void){
1190
1426
  mSCTP = rb_define_module("SCTP");
1191
1427
  cSocket = rb_define_class_under(mSCTP, "Socket", rb_cObject);
1192
1428
 
@@ -1258,19 +1494,32 @@ void Init_socket(){
1258
1494
  "ttl", "tsn", "cumtsn", "association_id", NULL
1259
1495
  );
1260
1496
 
1497
+ v_sctp_event_subscribe_struct = rb_struct_define(
1498
+ "EventSubscriptions", "data_io", "association", "address", "send_failure",
1499
+ "peer_error", "shutdown", "partial_delivery", "adaptation_layer",
1500
+ "authentication", "sender_dry", "stream_reset", "assoc_reset",
1501
+ "stream_change", "send_failure_event", NULL
1502
+ );
1503
+
1504
+ v_sctp_receive_info_struct = rb_struct_define(
1505
+ "ReceiveInfo", "message", "sid", "ssn", "flags", "ppid", "tsn",
1506
+ "cumtsn", "context", "assocation_id", NULL
1507
+ );
1508
+
1261
1509
  rb_define_method(cSocket, "initialize", rsctp_init, -1);
1262
1510
 
1263
- rb_define_method(cSocket, "bind", rsctp_bind, -1);
1511
+ rb_define_method(cSocket, "bindx", rsctp_bindx, -1);
1264
1512
  rb_define_method(cSocket, "close", rsctp_close, 0);
1265
- rb_define_method(cSocket, "connect", rsctp_connect, -1);
1266
- rb_define_method(cSocket, "getpeernames", rsctp_getpeernames, 0);
1267
- rb_define_method(cSocket, "getlocalnames", rsctp_getlocalnames, 0);
1513
+ rb_define_method(cSocket, "connectx", rsctp_connectx, -1);
1514
+ rb_define_method(cSocket, "getpeernames", rsctp_getpeernames, -1);
1515
+ rb_define_method(cSocket, "getlocalnames", rsctp_getlocalnames, -1);
1268
1516
  rb_define_method(cSocket, "get_status", rsctp_get_status, 0);
1269
1517
  rb_define_method(cSocket, "get_default_send_params", rsctp_get_default_send_params, 0);
1270
1518
  rb_define_method(cSocket, "get_retransmission_info", rsctp_get_retransmission_info, 0);
1271
1519
  rb_define_method(cSocket, "get_association_info", rsctp_get_association_info, 0);
1520
+ rb_define_method(cSocket, "get_subscriptions", rsctp_get_subscriptions, 0);
1272
1521
  rb_define_method(cSocket, "listen", rsctp_listen, -1);
1273
- rb_define_method(cSocket, "peeloff!", rsctp_peeloff, 1);
1522
+ rb_define_method(cSocket, "peeloff", rsctp_peeloff, 1);
1274
1523
  rb_define_method(cSocket, "recvmsg", rsctp_recvmsg, -1);
1275
1524
  rb_define_method(cSocket, "send", rsctp_send, 1);
1276
1525
 
@@ -1278,6 +1527,10 @@ void Init_socket(){
1278
1527
  rb_define_method(cSocket, "sendv", rsctp_sendv, 1);
1279
1528
  #endif
1280
1529
 
1530
+ #ifdef HAVE_SCTP_RECVV
1531
+ rb_define_method(cSocket, "recvv", rsctp_recvv, -1);
1532
+ #endif
1533
+
1281
1534
  rb_define_method(cSocket, "sendmsg", rsctp_sendmsg, 1);
1282
1535
  rb_define_method(cSocket, "set_initmsg", rsctp_set_initmsg, 1);
1283
1536
  rb_define_method(cSocket, "shutdown", rsctp_shutdown, -1);
@@ -1285,12 +1538,12 @@ void Init_socket(){
1285
1538
 
1286
1539
  rb_define_attr(cSocket, "domain", 1, 1);
1287
1540
  rb_define_attr(cSocket, "type", 1, 1);
1288
- rb_define_attr(cSocket, "sock_fd", 1, 1);
1541
+ rb_define_attr(cSocket, "fileno", 1, 1);
1289
1542
  rb_define_attr(cSocket, "association_id", 1, 1);
1290
1543
  rb_define_attr(cSocket, "port", 1, 1);
1291
1544
 
1292
- /* 0.0.5: The version of this library */
1293
- rb_define_const(cSocket, "VERSION", rb_str_new2("0.0.5"));
1545
+ /* 0.0.7: The version of this library */
1546
+ rb_define_const(cSocket, "VERSION", rb_str_new2("0.0.7"));
1294
1547
 
1295
1548
  /* send flags */
1296
1549
 
@@ -1322,4 +1575,9 @@ void Init_socket(){
1322
1575
  rb_define_const(cSocket, "SCTP_SHUTDOWN_SENT", INT2NUM(SCTP_SHUTDOWN_SENT));
1323
1576
  rb_define_const(cSocket, "SCTP_SHUTDOWN_RECEIVED", INT2NUM(SCTP_SHUTDOWN_RECEIVED));
1324
1577
  rb_define_const(cSocket, "SCTP_SHUTDOWN_ACK_SENT", INT2NUM(SCTP_SHUTDOWN_ACK_SENT));
1578
+
1579
+ // BINDING //
1580
+
1581
+ rb_define_const(cSocket, "SCTP_BINDX_ADD_ADDR", INT2NUM(SCTP_BINDX_ADD_ADDR));
1582
+ rb_define_const(cSocket, "SCTP_BINDX_REM_ADDR", INT2NUM(SCTP_BINDX_REM_ADDR));
1325
1583
  }
data/sctp-socket.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = 'sctp-socket'
3
- spec.version = '0.0.5'
3
+ spec.version = '0.0.7'
4
4
  spec.author = 'Daniel Berger'
5
5
  spec.email = 'djberg96@gmail.com'
6
6
  spec.summary = 'Ruby bindings for SCTP sockets'
@@ -19,6 +19,16 @@ Gem::Specification.new do |spec|
19
19
  spec.add_development_dependency 'rake-compiler', '~> 1.1'
20
20
  spec.add_development_dependency 'rspec', '~> 3.9'
21
21
 
22
+ spec.metadata = {
23
+ 'homepage_uri' => 'https://github.com/djberg96/sctp-socket',
24
+ 'bug_tracker_uri' => 'https://github.com/djberg96/sctp-socket/issues',
25
+ 'changelog_uri' => 'https://github.com/djberg96/sctp-socket/blob/main/CHANGES.md',
26
+ 'documentation_uri' => 'https://github.com/djberg96/sctp-socket/wiki',
27
+ 'source_code_uri' => 'https://github.com/djberg96/sctp-socket',
28
+ 'wiki_uri' => 'https://github.com/djberg96/sctp-socket/wiki',
29
+ 'rubygems_mfa_required' => 'true'
30
+ }
31
+
22
32
  spec.description = <<-EOF
23
33
  The sctp-socket library provides Ruby bindings for SCTP sockets. is a
24
34
  message oriented, reliable transport protocol with direct support for
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sctp-socket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Berger
@@ -35,7 +35,7 @@ cert_chain:
35
35
  ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
36
36
  WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
37
37
  -----END CERTIFICATE-----
38
- date: 2021-12-15 00:00:00.000000000 Z
38
+ date: 2024-05-28 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: bundler
@@ -103,6 +103,7 @@ extensions:
103
103
  - ext/sctp/extconf.rb
104
104
  extra_rdoc_files: []
105
105
  files:
106
+ - ".github/FUNDING.yml"
106
107
  - ".gitignore"
107
108
  - CHANGES.md
108
109
  - Gemfile
@@ -122,7 +123,14 @@ files:
122
123
  homepage: https://github.com/djberg96/sctp-socket
123
124
  licenses:
124
125
  - Apache-2.0
125
- metadata: {}
126
+ metadata:
127
+ homepage_uri: https://github.com/djberg96/sctp-socket
128
+ bug_tracker_uri: https://github.com/djberg96/sctp-socket/issues
129
+ changelog_uri: https://github.com/djberg96/sctp-socket/blob/main/CHANGES.md
130
+ documentation_uri: https://github.com/djberg96/sctp-socket/wiki
131
+ source_code_uri: https://github.com/djberg96/sctp-socket
132
+ wiki_uri: https://github.com/djberg96/sctp-socket/wiki
133
+ rubygems_mfa_required: 'true'
126
134
  post_install_message:
127
135
  rdoc_options: []
128
136
  require_paths:
@@ -138,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
146
  - !ruby/object:Gem::Version
139
147
  version: '0'
140
148
  requirements: []
141
- rubygems_version: 3.2.25
149
+ rubygems_version: 3.4.19
142
150
  signing_key:
143
151
  specification_version: 4
144
152
  summary: Ruby bindings for SCTP sockets
metadata.gz.sig CHANGED
Binary file