sctp-socket 0.0.5 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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