sctp-socket 0.1.4 → 0.2.0

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.
data/ext/sctp/socket.c CHANGED
@@ -39,14 +39,57 @@ VALUE v_sctp_initmsg_struct;
39
39
  #endif
40
40
  #endif
41
41
 
42
- // TODO: Yes, I know I need to update the signature.
42
+ // Memory safety and error handling macros
43
+ #define CHECK_FILENO_VALID(fileno) do { \
44
+ if ((fileno) < 0) { \
45
+ rb_raise(rb_eSystemCallError, "invalid file descriptor"); \
46
+ } \
47
+ } while(0)
48
+
49
+ #define SAFE_FREE(ptr) do { \
50
+ if ((ptr) != NULL) { \
51
+ free(ptr); \
52
+ (ptr) = NULL; \
53
+ } \
54
+ } while(0)
55
+
56
+ #define CHECK_SOCKET_CLOSED(self) do { \
57
+ VALUE v_fileno = rb_iv_get((self), "@fileno"); \
58
+ if (NIL_P(v_fileno) || NUM2INT(v_fileno) < 0) { \
59
+ rb_raise(rb_eIOError, "socket is closed"); \
60
+ } \
61
+ } while(0)
62
+
63
+ #define MAX_IP_ADDRESSES 8
64
+ #define DEFAULT_BUFFER_SIZE 1024
65
+ #define IP_BUFFER_SIZE INET6_ADDRSTRLEN
66
+
67
+ /*
68
+ * Convert a sockaddr_in structure to a Ruby struct.
69
+ * Handles both IPv4 and IPv6 addresses properly.
70
+ *
71
+ * @param addr Pointer to sockaddr_in structure
72
+ * @return Ruby struct representing the socket address
73
+ */
43
74
  VALUE convert_sockaddr_in_to_struct(struct sockaddr_in* addr){
44
- char ipbuf[INET6_ADDRSTRLEN];
75
+ char ipbuf[IP_BUFFER_SIZE];
76
+ const char* result;
77
+
78
+ if(addr == NULL)
79
+ rb_raise(rb_eArgError, "null address pointer");
80
+
81
+ if((addr->sin_family != AF_INET) && (addr->sin_family != AF_INET6))
82
+ rb_raise(rb_eArgError, "unsupported address family");
83
+
84
+ bzero(ipbuf, sizeof(ipbuf));
45
85
 
46
86
  if(addr->sin_family == AF_INET6)
47
- inet_ntop(addr->sin_family, &(((struct sockaddr_in6 *)addr)->sin6_addr), ipbuf, sizeof(ipbuf));
87
+ result = inet_ntop(addr->sin_family, &(((struct sockaddr_in6 *)addr)->sin6_addr), ipbuf, sizeof(ipbuf));
48
88
  else
49
- inet_ntop(addr->sin_family, &(((struct sockaddr_in *)addr)->sin_addr), ipbuf, sizeof(ipbuf));
89
+ result = inet_ntop(addr->sin_family, &(((struct sockaddr_in *)addr)->sin_addr), ipbuf, sizeof(ipbuf));
90
+
91
+ if(result == NULL)
92
+ rb_raise(rb_eSystemCallError, "inet_ntop: %s", strerror(errno));
50
93
 
51
94
  return rb_struct_new(v_sockaddr_in_struct,
52
95
  INT2NUM(addr->sin_family),
@@ -55,27 +98,49 @@ VALUE convert_sockaddr_in_to_struct(struct sockaddr_in* addr){
55
98
  );
56
99
  }
57
100
 
58
- // Helper function to get a hash value via string or symbol.
101
+ /*
102
+ * Helper function to get a hash value via string or symbol key.
103
+ * This provides Ruby's flexible hash access pattern.
104
+ *
105
+ * @param v_hash Ruby hash object
106
+ * @param key String key to look up
107
+ * @return Ruby value or Qnil if not found
108
+ */
59
109
  VALUE rb_hash_aref2(VALUE v_hash, const char* key){
60
110
  VALUE v_key, v_val;
61
111
 
112
+ if(key == NULL)
113
+ return Qnil;
114
+
115
+ // Try a string key first
62
116
  v_key = rb_str_new2(key);
63
117
  v_val = rb_hash_aref(v_hash, v_key);
64
118
 
119
+ // If not found, try a symbol key
65
120
  if(NIL_P(v_val))
66
121
  v_val = rb_hash_aref(v_hash, ID2SYM(rb_intern(key)));
67
122
 
68
123
  return v_val;
69
124
  }
70
125
 
126
+ /*
127
+ * Parse and convert SCTP notification messages into Ruby structures.
128
+ * This function handles various types of SCTP notifications.
129
+ *
130
+ * @param buffer Raw notification buffer from SCTP
131
+ * @return Ruby struct representing the notification
132
+ */
71
133
  VALUE get_notification_info(char* buffer){
72
134
  uint32_t i;
73
- char str[16];
135
+ char str[IP_BUFFER_SIZE];
74
136
  union sctp_notification* snp;
75
137
  VALUE v_notification = Qnil;
76
138
  VALUE v_str = Qnil;
77
139
  VALUE* v_temp;
78
140
 
141
+ if(buffer == NULL)
142
+ rb_raise(rb_eArgError, "notification buffer is null");
143
+
79
144
  snp = (union sctp_notification*)buffer;
80
145
 
81
146
  switch(snp->sn_header.sn_type){
@@ -310,11 +375,24 @@ static VALUE rsctp_init(int argc, VALUE* argv, VALUE self){
310
375
 
311
376
  rb_scan_args(argc, argv, "02", &v_domain, &v_type);
312
377
 
313
- if(NIL_P(v_domain))
378
+ // Set defaults with validation
379
+ if(NIL_P(v_domain)){
314
380
  v_domain = INT2NUM(AF_INET);
315
-
316
- if(NIL_P(v_type))
381
+ }
382
+ else{
383
+ int domain = NUM2INT(v_domain);
384
+ if((domain != AF_INET) && (domain != AF_INET6))
385
+ rb_raise(rb_eArgError, "unsupported domain family: %d", domain);
386
+ }
387
+
388
+ if(NIL_P(v_type)){
317
389
  v_type = INT2NUM(SOCK_SEQPACKET);
390
+ }
391
+ else{
392
+ int type = NUM2INT(v_type);
393
+ if (type != SOCK_SEQPACKET && type != SOCK_STREAM)
394
+ rb_raise(rb_eArgError, "unsupported socket type: %d", type);
395
+ }
318
396
 
319
397
  fileno = socket(NUM2INT(v_domain), NUM2INT(v_type), IPPROTO_SCTP);
320
398
 
@@ -373,7 +451,7 @@ static VALUE rsctp_init(int argc, VALUE* argv, VALUE self){
373
451
  * Returns the port that it was bound to.
374
452
  */
375
453
  static VALUE rsctp_bindx(int argc, VALUE* argv, VALUE self){
376
- struct sockaddr_in addrs[8];
454
+ struct sockaddr_in addrs[MAX_IP_ADDRESSES];
377
455
  int i, fileno, num_ip, flags, domain, port, on;
378
456
  VALUE v_addresses, v_port, v_flags, v_address, v_reuse_addr, v_options;
379
457
 
@@ -404,7 +482,7 @@ static VALUE rsctp_bindx(int argc, VALUE* argv, VALUE self){
404
482
  else
405
483
  num_ip = (int)RARRAY_LEN(v_addresses);
406
484
 
407
- if(num_ip > 8)
485
+ if(num_ip > MAX_IP_ADDRESSES)
408
486
  rb_raise(rb_eArgError, "too many IP addresses to bind, maximum is eight");
409
487
 
410
488
  domain = NUM2INT(rb_iv_get(self, "@domain"));
@@ -472,7 +550,7 @@ static VALUE rsctp_bindx(int argc, VALUE* argv, VALUE self){
472
550
  * methods will automatically establish associations.
473
551
  */
474
552
  static VALUE rsctp_connectx(int argc, VALUE* argv, VALUE self){
475
- struct sockaddr_in addrs[8];
553
+ struct sockaddr_in addrs[MAX_IP_ADDRESSES];
476
554
  int i, num_ip, fileno;
477
555
  sctp_assoc_t assoc;
478
556
  VALUE v_address, v_domain, v_options, v_addresses, v_port;
@@ -540,7 +618,7 @@ static VALUE rsctp_connectx(int argc, VALUE* argv, VALUE self){
540
618
  * socket.close(linger: 5)
541
619
  */
542
620
  static VALUE rsctp_close(int argc, VALUE* argv, VALUE self){
543
- VALUE v_options, v_linger;
621
+ VALUE v_options, v_linger, v_fileno;
544
622
  int fileno;
545
623
 
546
624
  rb_scan_args(argc, argv, "01", &v_options);
@@ -551,27 +629,56 @@ static VALUE rsctp_close(int argc, VALUE* argv, VALUE self){
551
629
  Check_Type(v_options, T_HASH);
552
630
 
553
631
  v_linger = rb_hash_aref2(v_options, "linger");
632
+ v_fileno = rb_iv_get(self, "@fileno");
554
633
 
555
- fileno = NUM2INT(rb_iv_get(self, "@fileno"));
634
+ if(NIL_P(v_fileno)) // Already closed
635
+ return self;
636
+
637
+ fileno = NUM2INT(v_fileno);
556
638
 
557
639
  if(!NIL_P(v_linger)){
558
640
  struct linger lin;
641
+ int linger_time = NUM2INT(v_linger);
642
+
643
+ if(linger_time < 0)
644
+ rb_raise(rb_eArgError, "linger time must be non-negative");
645
+
559
646
  lin.l_onoff = 1;
560
- lin.l_linger = NUM2INT(v_linger);
647
+ lin.l_linger = linger_time;
561
648
 
562
649
  if(setsockopt(fileno, SOL_SOCKET, SO_LINGER, &lin, sizeof(struct linger)) < 0)
563
650
  rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
564
651
  }
565
652
 
566
- if(close(fileno))
653
+ if(close(fileno) < 0)
567
654
  rb_raise(rb_eSystemCallError, "close: %s", strerror(errno));
568
655
 
656
+ // Mark socket as closed
657
+ rb_iv_set(self, "@fileno", Qnil);
658
+
569
659
  return self;
570
660
  }
571
661
 
572
662
  /*
573
663
  * call-seq:
574
- * SCTP::Socket#getpeernames
664
+ * SCTP::Socket#closed?
665
+ *
666
+ * Returns true if the socket is closed, false otherwise.
667
+ *
668
+ * Example:
669
+ * socket = SCTP::Socket.new
670
+ * socket.closed? # => false
671
+ * socket.close
672
+ * socket.closed? # => true
673
+ */
674
+ static VALUE rsctp_closed_p(VALUE self){
675
+ VALUE v_fileno = rb_iv_get(self, "@fileno");
676
+ return NIL_P(v_fileno) ? Qtrue : Qfalse;
677
+ }
678
+
679
+ /*
680
+ * call-seq:
681
+ * SCTP::Socket#getpeernames
575
682
  *
576
683
  * Return an array of all addresses of a peer of the current socket
577
684
  * and association number.
@@ -584,7 +691,7 @@ static VALUE rsctp_close(int argc, VALUE* argv, VALUE self){
584
691
  * socket = SCTP::Socket.new
585
692
  * # ...
586
693
  * p socket.getpeernames
587
- *
694
+ *
588
695
  * info = socket.recvmsg
589
696
  * association_fileno = socket.peeloff(info.association_id)
590
697
  *
@@ -592,20 +699,22 @@ static VALUE rsctp_close(int argc, VALUE* argv, VALUE self){
592
699
  */
593
700
  static VALUE rsctp_getpeernames(int argc, VALUE* argv, VALUE self){
594
701
  sctp_assoc_t assoc_id;
595
- struct sockaddr* addrs;
702
+ struct sockaddr* addrs = NULL;
596
703
  int i, fileno, num_addrs;
597
- char str[16];
704
+ char str[IP_BUFFER_SIZE];
598
705
  VALUE v_fileno, v_association_id;
599
706
  VALUE v_array = rb_ary_new();
600
707
 
601
- bzero(&addrs, sizeof(addrs));
602
-
603
708
  rb_scan_args(argc, argv, "02", &v_fileno, &v_association_id);
604
709
 
605
- if(NIL_P(v_fileno))
710
+ if(NIL_P(v_fileno)){
711
+ CHECK_SOCKET_CLOSED(self);
606
712
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
607
- else
713
+ }
714
+ else{
608
715
  fileno = NUM2INT(v_fileno);
716
+ CHECK_FILENO_VALID(fileno);
717
+ }
609
718
 
610
719
  if(NIL_P(v_association_id))
611
720
  assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
@@ -615,14 +724,16 @@ static VALUE rsctp_getpeernames(int argc, VALUE* argv, VALUE self){
615
724
  num_addrs = sctp_getpaddrs(fileno, assoc_id, &addrs);
616
725
 
617
726
  if(num_addrs < 0){
618
- sctp_freepaddrs(addrs);
727
+ if(addrs != NULL)
728
+ sctp_freepaddrs(addrs);
729
+
619
730
  rb_raise(rb_eSystemCallError, "sctp_getpaddrs: %s", strerror(errno));
620
731
  }
621
732
 
622
733
  for(i = 0; i < num_addrs; i++){
734
+ bzero(&str, sizeof(str));
623
735
  inet_ntop(AF_INET, &(((struct sockaddr_in *)&addrs[i])->sin_addr), str, sizeof(str));
624
736
  rb_ary_push(v_array, rb_str_new2(str));
625
- bzero(&str, sizeof(str));
626
737
  }
627
738
 
628
739
  sctp_freepaddrs(addrs);
@@ -649,20 +760,22 @@ static VALUE rsctp_getpeernames(int argc, VALUE* argv, VALUE self){
649
760
  */
650
761
  static VALUE rsctp_getlocalnames(int argc, VALUE* argv, VALUE self){
651
762
  sctp_assoc_t assoc_id;
652
- struct sockaddr* addrs;
763
+ struct sockaddr* addrs = NULL;
653
764
  int i, fileno, num_addrs;
654
- char str[16];
765
+ char str[IP_BUFFER_SIZE];
655
766
  VALUE v_assoc_fileno, v_assoc_id;
656
767
  VALUE v_array = rb_ary_new();
657
768
 
658
- bzero(&addrs, sizeof(addrs));
659
-
660
769
  rb_scan_args(argc, argv, "02", &v_assoc_fileno, &v_assoc_id);
661
770
 
662
- if(NIL_P(v_assoc_fileno))
771
+ if(NIL_P(v_assoc_fileno)){
772
+ CHECK_SOCKET_CLOSED(self);
663
773
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
664
- else
774
+ }
775
+ else{
665
776
  fileno = NUM2INT(v_assoc_fileno);
777
+ CHECK_FILENO_VALID(fileno);
778
+ }
666
779
 
667
780
  if(NIL_P(v_assoc_id))
668
781
  assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
@@ -672,14 +785,16 @@ static VALUE rsctp_getlocalnames(int argc, VALUE* argv, VALUE self){
672
785
  num_addrs = sctp_getladdrs(fileno, assoc_id, &addrs);
673
786
 
674
787
  if(num_addrs < 0){
675
- sctp_freeladdrs(addrs);
788
+ if(addrs != NULL)
789
+ sctp_freeladdrs(addrs);
790
+
676
791
  rb_raise(rb_eSystemCallError, "sctp_getladdrs: %s", strerror(errno));
677
792
  }
678
793
 
679
794
  for(i = 0; i < num_addrs; i++){
795
+ bzero(&str, sizeof(str));
680
796
  inet_ntop(AF_INET, &(((struct sockaddr_in *)&addrs[i])->sin_addr), str, sizeof(str));
681
797
  rb_ary_push(v_array, rb_str_new2(str));
682
- bzero(&str, sizeof(str));
683
798
  }
684
799
 
685
800
  sctp_freeladdrs(addrs);
@@ -704,11 +819,11 @@ static VALUE rsctp_getlocalnames(int argc, VALUE* argv, VALUE self){
704
819
  * socket = SCTP::Socket.new
705
820
  *
706
821
  * # You can specify addresses here or in an earlier connectx call.
707
- * socket.sendv
822
+ * socket.sendv({
708
823
  * :message => ['Hello ', 'World.'],
709
824
  * :addresses => ['10.0.5.4', '10.0.6.4'],
710
825
  * :info_type => SCTP::Socket:::SCTP_SENDV_SNDINFO
711
- * )
826
+ * })
712
827
  *
713
828
  * CAVEAT: Currently info_type is not yet supported.
714
829
  *
@@ -730,8 +845,11 @@ static VALUE rsctp_sendv(VALUE self, VALUE v_options){
730
845
  v_message = rb_hash_aref2(v_options, "message");
731
846
  v_addresses = rb_hash_aref2(v_options, "addresses");
732
847
 
733
- if(!NIL_P(v_message))
734
- Check_Type(v_message, T_ARRAY);
848
+ // Validate required message parameter
849
+ if(NIL_P(v_message))
850
+ rb_raise(rb_eArgError, "message parameter is required");
851
+
852
+ Check_Type(v_message, T_ARRAY);
735
853
 
736
854
  if(!NIL_P(v_addresses)){
737
855
  Check_Type(v_addresses, T_ARRAY);
@@ -982,7 +1100,7 @@ static VALUE rsctp_send(VALUE self, VALUE v_options){
982
1100
  * :stream -> The SCTP stream number you wish to send the message on.
983
1101
  * :addresses -> An array of addresses to send the message to.
984
1102
  * :context -> The default context used for the sendmsg call if the send fails.
985
- * :ppid -> The payload protocol identifier that is passed to the peer endpoint.
1103
+ * :ppid -> The payload protocol identifier that is passed to the peer endpoint.
986
1104
  * :flags -> A bitwise integer that contain one or more values that control behavior.
987
1105
  *
988
1106
  * Note that the :addresses option is not mandatory in a one-to-one (SOCK_STREAM)
@@ -1008,7 +1126,7 @@ static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
1008
1126
  uint16_t stream;
1009
1127
  uint32_t ppid, flags, ttl, context;
1010
1128
  ssize_t num_bytes;
1011
- struct sockaddr_in addrs[8];
1129
+ struct sockaddr_in addrs[MAX_IP_ADDRESSES];
1012
1130
  int fileno, size, num_ip;
1013
1131
 
1014
1132
  Check_Type(v_options, T_HASH);
@@ -1023,6 +1141,9 @@ static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
1023
1141
  v_ttl = rb_hash_aref2(v_options, "ttl");
1024
1142
  v_addresses = rb_hash_aref2(v_options, "addresses");
1025
1143
 
1144
+ if(NIL_P(v_msg))
1145
+ rb_raise(rb_eArgError, "message parameter is required");
1146
+
1026
1147
  if(NIL_P(v_stream))
1027
1148
  stream = 0;
1028
1149
  else
@@ -1055,6 +1176,7 @@ static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
1055
1176
  int i, port;
1056
1177
  VALUE v_address, v_port;
1057
1178
 
1179
+ Check_Type(v_addresses, T_ARRAY);
1058
1180
  num_ip = (int)RARRAY_LEN(v_addresses);
1059
1181
  v_port = rb_hash_aref2(v_options, "port");
1060
1182
 
@@ -1176,7 +1298,7 @@ static VALUE rsctp_recvmsg(int argc, VALUE* argv, VALUE self){
1176
1298
  if(NIL_P(v_flags))
1177
1299
  flags = 0;
1178
1300
  else
1179
- flags = NUM2INT(v_flags);
1301
+ flags = NUM2INT(v_flags);
1180
1302
 
1181
1303
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1182
1304
  length = sizeof(struct sockaddr_in);
@@ -1305,7 +1427,7 @@ static VALUE rsctp_set_initmsg(VALUE self, VALUE v_options){
1305
1427
  * :peer_error (aka remote error)
1306
1428
  *
1307
1429
  * Example:
1308
- *
1430
+ *
1309
1431
  * socket = SCTP::Socket.new
1310
1432
  *
1311
1433
  * socket.bind(:port => port, :addresses => ['127.0.0.1'])
@@ -1402,7 +1524,7 @@ static VALUE rsctp_listen(int argc, VALUE* argv, VALUE self){
1402
1524
 
1403
1525
  if(listen(fileno, backlog) < 0)
1404
1526
  rb_raise(rb_eSystemCallError, "listen: %s", strerror(errno));
1405
-
1527
+
1406
1528
  return self;
1407
1529
  }
1408
1530
 
@@ -1427,7 +1549,7 @@ static VALUE rsctp_listen(int argc, VALUE* argv, VALUE self){
1427
1549
  static VALUE rsctp_peeloff(VALUE self, VALUE v_assoc_id){
1428
1550
  int fileno, assoc_fileno;
1429
1551
  sctp_assoc_t assoc_id;
1430
-
1552
+
1431
1553
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1432
1554
  assoc_id = NUM2INT(v_assoc_id);
1433
1555
 
@@ -1884,7 +2006,7 @@ static VALUE rsctp_get_subscriptions(VALUE self){
1884
2006
  */
1885
2007
  static VALUE rsctp_get_peer_address_params(VALUE self){
1886
2008
  int fileno;
1887
- char str[16];
2009
+ char str[IP_BUFFER_SIZE];
1888
2010
  socklen_t size;
1889
2011
  sctp_assoc_t assoc_id;
1890
2012
  struct sctp_paddrparams paddr;
@@ -1991,11 +2113,9 @@ static VALUE rsctp_get_nodelay(VALUE self){
1991
2113
  static VALUE rsctp_set_nodelay(VALUE self, VALUE v_bool){
1992
2114
  int fileno;
1993
2115
  socklen_t size;
1994
- sctp_assoc_t assoc_id;
1995
2116
  int value;
1996
2117
 
1997
2118
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1998
- assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
1999
2119
  size = sizeof(int);
2000
2120
 
2001
2121
  if(NIL_P(v_bool) || v_bool == Qfalse)
@@ -2003,8 +2123,8 @@ static VALUE rsctp_set_nodelay(VALUE self, VALUE v_bool){
2003
2123
  else
2004
2124
  value = 1;
2005
2125
 
2006
- if(sctp_opt_info(fileno, assoc_id, SCTP_NODELAY, (void*)&value, &size) < 0)
2007
- rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
2126
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_NODELAY, &value, size) < 0)
2127
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2008
2128
 
2009
2129
  if(value)
2010
2130
  return Qtrue;
@@ -2015,7 +2135,7 @@ static VALUE rsctp_set_nodelay(VALUE self, VALUE v_bool){
2015
2135
  /*
2016
2136
  * call-seq:
2017
2137
  * SCTP::Socket#disable_fragments=(bool)
2018
- *
2138
+ *
2019
2139
  * This option is a on/off flag and is passed an integer where a non-
2020
2140
  * zero is on and a zero is off. If enabled no SCTP message
2021
2141
  * fragmentation will be performed. Instead if a message being sent
@@ -2093,19 +2213,15 @@ static VALUE rsctp_get_autoclose(VALUE self){
2093
2213
  */
2094
2214
  static VALUE rsctp_set_autoclose(VALUE self, VALUE v_seconds){
2095
2215
  int fileno;
2096
- socklen_t size;
2097
- sctp_assoc_t assoc_id;
2098
2216
  int value;
2099
2217
 
2100
2218
  value = NUM2INT(v_seconds);
2101
2219
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2102
- assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
2103
- size = sizeof(int);
2104
2220
 
2105
- if(sctp_opt_info(fileno, assoc_id, SCTP_AUTOCLOSE, (void*)&value, &size) < 0)
2106
- rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
2221
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_AUTOCLOSE, &value, sizeof(value)) < 0)
2222
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2107
2223
 
2108
- return v_seconds;
2224
+ return INT2NUM(value);
2109
2225
  }
2110
2226
 
2111
2227
  /*
@@ -2140,6 +2256,43 @@ static VALUE rsctp_enable_auth_support(int argc, VALUE* argv, VALUE self){
2140
2256
  return self;
2141
2257
  }
2142
2258
 
2259
+ /*
2260
+ * call-seq:
2261
+ * SCTP::Socket#auth_support?(association_id=nil)
2262
+ *
2263
+ * Returns whether or not authentication support is enabled for the association.
2264
+ * Returns true if auth support is enabled, false otherwise.
2265
+ */
2266
+ static VALUE rsctp_get_auth_support(int argc, VALUE* argv, VALUE self){
2267
+ int fileno;
2268
+ socklen_t size;
2269
+ sctp_assoc_t assoc_id;
2270
+ struct sctp_assoc_value assoc_value;
2271
+ VALUE v_assoc_id;
2272
+
2273
+ rb_scan_args(argc, argv, "01", &v_assoc_id);
2274
+
2275
+ CHECK_SOCKET_CLOSED(self);
2276
+
2277
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2278
+ size = sizeof(struct sctp_assoc_value);
2279
+
2280
+ if(NIL_P(v_assoc_id))
2281
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
2282
+ else
2283
+ assoc_id = NUM2INT(v_assoc_id);
2284
+
2285
+ assoc_value.assoc_id = assoc_id;
2286
+
2287
+ if(sctp_opt_info(fileno, assoc_id, SCTP_AUTH_SUPPORTED, (void*)&assoc_value, &size) < 0)
2288
+ rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
2289
+
2290
+ if(assoc_value.assoc_value)
2291
+ return Qtrue;
2292
+ else
2293
+ return Qfalse;
2294
+ }
2295
+
2143
2296
  /*
2144
2297
  * call-seq:
2145
2298
  * SCTP::Socket#set_shared_key(key, keynum, association_id=nil)
@@ -2203,7 +2356,7 @@ static VALUE rsctp_set_shared_key(int argc, VALUE* argv, VALUE self){
2203
2356
 
2204
2357
  auth_key->sca_assoc_id = assoc_id;
2205
2358
  auth_key->sca_keynumber = keynum;
2206
- auth_key->sca_keylength = strlen(key);
2359
+ auth_key->sca_keylength = strlen(key);
2207
2360
  memcpy(auth_key->sca_key, byte_array, sizeof(byte_array));
2208
2361
 
2209
2362
  if(sctp_opt_info(fileno, assoc_id, SCTP_AUTH_KEY, (void*)auth_key, &size) < 0)
@@ -2366,22 +2519,228 @@ static VALUE rsctp_delete_shared_key(int argc, VALUE* argv, VALUE self){
2366
2519
  */
2367
2520
  static VALUE rsctp_map_ipv4(VALUE self, VALUE v_bool){
2368
2521
  int fileno, boolean;
2369
- sctp_assoc_t assoc_id;
2370
- socklen_t size;
2371
2522
 
2372
2523
  boolean = 0;
2373
2524
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2374
- assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
2375
2525
 
2376
2526
  if(v_bool == Qtrue)
2377
2527
  boolean = 1;
2378
2528
 
2379
- if(sctp_opt_info(fileno, assoc_id, SCTP_I_WANT_MAPPED_V4_ADDR, (void*)&boolean, &size) < 0)
2380
- rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
2529
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, (void*)&boolean, sizeof(boolean)) < 0)
2530
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2381
2531
 
2382
2532
  return v_bool;
2383
2533
  }
2384
2534
 
2535
+ /*
2536
+ * call-seq:
2537
+ * SCTP::Socket#map_ipv4?
2538
+ *
2539
+ * Returns whether or not IPv4 addresses will be mapped to V6 representation
2540
+ * for PF_INET6 sockets. Returns true if mapping is enabled, false otherwise.
2541
+ */
2542
+ static VALUE rsctp_get_map_ipv4(VALUE self){
2543
+ int fileno;
2544
+ socklen_t size;
2545
+ sctp_assoc_t assoc_id;
2546
+ int value;
2547
+
2548
+ CHECK_SOCKET_CLOSED(self);
2549
+
2550
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2551
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
2552
+ size = sizeof(int);
2553
+
2554
+ if(sctp_opt_info(fileno, assoc_id, SCTP_I_WANT_MAPPED_V4_ADDR, (void*)&value, &size) < 0)
2555
+ rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
2556
+
2557
+ if(value)
2558
+ return Qtrue;
2559
+ else
2560
+ return Qfalse;
2561
+ }
2562
+
2563
+ /*
2564
+ * call-seq:
2565
+ * SCTP::Socket#set_default_send_params(options)
2566
+ *
2567
+ * Sets the default parameters that will be used by the sendmsg() call
2568
+ * when it is invoked with a null parameter.
2569
+ *
2570
+ * The +options+ hash may contain the following keys:
2571
+ *
2572
+ * * stream: A number indicating the stream number within the association
2573
+ * * ssn: Not used by applications. Ignored for one-to-many style sockets.
2574
+ * * flags: Indicates various options for sending. See SCTP_* constants.
2575
+ * * ppid: The payload protocol identifier
2576
+ * * context: User-specified context information
2577
+ * * ttl: The time to live (in milliseconds)
2578
+ * * tsn: Not used by applications
2579
+ * * cumtsn: Not used by applications
2580
+ * * association_id: The association identification (ignored for one-to-one sockets)
2581
+ *
2582
+ * Example:
2583
+ *
2584
+ * socket.set_default_send_params(:stream => 1, :flags => SCTP::Socket::SCTP_UNORDERED)
2585
+ */
2586
+ static VALUE rsctp_set_default_send_params(VALUE self, VALUE v_options){
2587
+ VALUE v_stream, v_ssn, v_flags, v_ppid, v_context, v_ttl, v_tsn, v_cumtsn, v_assoc_id;
2588
+ int fileno;
2589
+ sctp_assoc_t assoc_id;
2590
+ struct sctp_sndrcvinfo sndrcv;
2591
+
2592
+ if(!RB_TYPE_P(v_options, T_HASH))
2593
+ rb_raise(rb_eTypeError, "options must be a hash");
2594
+
2595
+ bzero(&sndrcv, sizeof(sndrcv));
2596
+
2597
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2598
+
2599
+ v_stream = rb_hash_aref2(v_options, "stream");
2600
+ v_ssn = rb_hash_aref2(v_options, "ssn");
2601
+ v_flags = rb_hash_aref2(v_options, "flags");
2602
+ v_ppid = rb_hash_aref2(v_options, "ppid");
2603
+ v_context = rb_hash_aref2(v_options, "context");
2604
+ v_ttl = rb_hash_aref2(v_options, "ttl");
2605
+ v_tsn = rb_hash_aref2(v_options, "tsn");
2606
+ v_cumtsn = rb_hash_aref2(v_options, "cumtsn");
2607
+ v_assoc_id = rb_hash_aref2(v_options, "association_id");
2608
+
2609
+ if(NIL_P(v_assoc_id))
2610
+ v_assoc_id = rb_iv_get(self, "@association_id");
2611
+
2612
+ assoc_id = NUM2INT(v_assoc_id);
2613
+ sndrcv.sinfo_assoc_id = assoc_id;
2614
+
2615
+ if(!NIL_P(v_stream))
2616
+ sndrcv.sinfo_stream = NUM2INT(v_stream);
2617
+
2618
+ if(!NIL_P(v_ssn))
2619
+ sndrcv.sinfo_ssn = NUM2INT(v_ssn);
2620
+
2621
+ if(!NIL_P(v_flags))
2622
+ sndrcv.sinfo_flags = NUM2INT(v_flags);
2623
+
2624
+ if(!NIL_P(v_ppid))
2625
+ sndrcv.sinfo_ppid = NUM2INT(v_ppid);
2626
+
2627
+ if(!NIL_P(v_context))
2628
+ sndrcv.sinfo_context = NUM2INT(v_context);
2629
+
2630
+ if(!NIL_P(v_ttl))
2631
+ sndrcv.sinfo_timetolive = NUM2INT(v_ttl);
2632
+
2633
+ if(!NIL_P(v_tsn))
2634
+ sndrcv.sinfo_tsn = NUM2INT(v_tsn);
2635
+
2636
+ if(!NIL_P(v_cumtsn))
2637
+ sndrcv.sinfo_cumtsn = NUM2INT(v_cumtsn);
2638
+
2639
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_DEFAULT_SEND_PARAM, &sndrcv, sizeof(sndrcv)) < 0)
2640
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2641
+
2642
+ return rb_struct_new(
2643
+ v_sctp_default_send_params_struct,
2644
+ v_stream,
2645
+ v_ssn,
2646
+ v_flags,
2647
+ v_ppid,
2648
+ v_context,
2649
+ v_ttl,
2650
+ v_tsn,
2651
+ v_cumtsn,
2652
+ v_assoc_id
2653
+ );
2654
+ }
2655
+
2656
+ /*
2657
+ * call-seq:
2658
+ * SCTP::Socket#set_peer_address_params(options)
2659
+ *
2660
+ * Sets the peer address parameters. This allows applications to enable or
2661
+ * disable heartbeats for any peer address of an association, set the maximum
2662
+ * number of retransmissions to a destination address, and set the path MTU.
2663
+ *
2664
+ * The +options+ hash may contain the following keys:
2665
+ *
2666
+ * * association_id: The association identification
2667
+ * * address: The address of the remote peer
2668
+ * * hbinterval: The heartbeat interval (in milliseconds)
2669
+ * * pathmaxrxt: The maximum number of retransmissions
2670
+ * * pathmtu: The path MTU
2671
+ * * flags: Flags to control heartbeats, etc.
2672
+ * * ipv6_flowlabel: IPv6 flow label (only for IPv6 addresses)
2673
+ *
2674
+ * Example:
2675
+ *
2676
+ * socket.set_peer_address_params(:hbinterval => 5000, :pathmaxrxt => 5)
2677
+ */
2678
+ static VALUE rsctp_set_peer_address_params(VALUE self, VALUE v_options){
2679
+ VALUE v_assoc_id, v_address, v_hbinterval, v_pathmaxrxt, v_pathmtu, v_flags, v_ipv6_flowlabel;
2680
+ int fileno;
2681
+ sctp_assoc_t assoc_id;
2682
+ struct sctp_paddrparams paddr;
2683
+ struct sockaddr_in* sin;
2684
+
2685
+ if(!RB_TYPE_P(v_options, T_HASH))
2686
+ rb_raise(rb_eTypeError, "options must be a hash");
2687
+
2688
+ bzero(&paddr, sizeof(paddr));
2689
+
2690
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2691
+
2692
+ v_assoc_id = rb_hash_aref2(v_options, "association_id");
2693
+ v_address = rb_hash_aref2(v_options, "address");
2694
+ v_hbinterval = rb_hash_aref2(v_options, "hbinterval");
2695
+ v_pathmaxrxt = rb_hash_aref2(v_options, "pathmaxrxt");
2696
+ v_pathmtu = rb_hash_aref2(v_options, "pathmtu");
2697
+ v_flags = rb_hash_aref2(v_options, "flags");
2698
+ v_ipv6_flowlabel = rb_hash_aref2(v_options, "ipv6_flowlabel");
2699
+
2700
+ if(NIL_P(v_assoc_id))
2701
+ v_assoc_id = rb_iv_get(self, "@association_id");
2702
+
2703
+ assoc_id = NUM2INT(v_assoc_id);
2704
+ paddr.spp_assoc_id = assoc_id;
2705
+
2706
+ // If address is provided, set up the sockaddr structure
2707
+ if(!NIL_P(v_address)){
2708
+ sin = (struct sockaddr_in*)&paddr.spp_address;
2709
+ sin->sin_family = AF_INET;
2710
+ if(inet_pton(AF_INET, StringValueCStr(v_address), &sin->sin_addr) <= 0)
2711
+ rb_raise(rb_eArgError, "invalid IP address");
2712
+ }
2713
+
2714
+ if(!NIL_P(v_hbinterval))
2715
+ paddr.spp_hbinterval = NUM2INT(v_hbinterval);
2716
+
2717
+ if(!NIL_P(v_pathmaxrxt))
2718
+ paddr.spp_pathmaxrxt = NUM2INT(v_pathmaxrxt);
2719
+
2720
+ if(!NIL_P(v_pathmtu))
2721
+ paddr.spp_pathmtu = NUM2INT(v_pathmtu);
2722
+
2723
+ if(!NIL_P(v_flags))
2724
+ paddr.spp_flags = NUM2INT(v_flags);
2725
+
2726
+ if(!NIL_P(v_ipv6_flowlabel))
2727
+ paddr.spp_ipv6_flowlabel = NUM2INT(v_ipv6_flowlabel);
2728
+
2729
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &paddr, sizeof(paddr)) < 0)
2730
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2731
+
2732
+ return rb_struct_new(
2733
+ v_sctp_peer_addr_params_struct,
2734
+ v_assoc_id,
2735
+ v_address,
2736
+ v_hbinterval,
2737
+ v_pathmaxrxt,
2738
+ v_pathmtu,
2739
+ v_flags,
2740
+ v_ipv6_flowlabel
2741
+ );
2742
+ }
2743
+
2385
2744
  void Init_socket(void){
2386
2745
  mSCTP = rb_define_module("SCTP");
2387
2746
  cSocket = rb_define_class_under(mSCTP, "Socket", rb_cObject);
@@ -2485,23 +2844,26 @@ void Init_socket(void){
2485
2844
  rb_define_method(cSocket, "autoclose=", rsctp_set_autoclose, 1);
2486
2845
  rb_define_method(cSocket, "bindx", rsctp_bindx, -1);
2487
2846
  rb_define_method(cSocket, "close", rsctp_close, -1);
2847
+ rb_define_method(cSocket, "closed?", rsctp_closed_p, 0);
2488
2848
  rb_define_method(cSocket, "connectx", rsctp_connectx, -1);
2489
2849
  rb_define_method(cSocket, "delete_shared_key", rsctp_delete_shared_key, -1);
2490
2850
  rb_define_method(cSocket, "disable_fragments=", rsctp_disable_fragments, 1);
2491
2851
  rb_define_method(cSocket, "enable_auth_support", rsctp_enable_auth_support, -1);
2852
+ rb_define_method(cSocket, "auth_support?", rsctp_get_auth_support, -1);
2492
2853
  rb_define_method(cSocket, "getpeernames", rsctp_getpeernames, -1);
2493
2854
  rb_define_method(cSocket, "getlocalnames", rsctp_getlocalnames, -1);
2494
2855
  rb_define_method(cSocket, "get_active_shared_key", rsctp_get_active_shared_key, -1);
2495
2856
  rb_define_method(cSocket, "get_association_info", rsctp_get_association_info, 0);
2496
2857
  rb_define_method(cSocket, "get_autoclose", rsctp_get_autoclose, 0);
2497
2858
  rb_define_method(cSocket, "get_default_send_params", rsctp_get_default_send_params, 0);
2498
- rb_define_method(cSocket, "get_initmsg", rsctp_get_init_msg, 0);
2859
+ rb_define_method(cSocket, "get_init_msg", rsctp_get_init_msg, 0);
2499
2860
  rb_define_method(cSocket, "get_peer_address_params", rsctp_get_peer_address_params, 0);
2500
2861
  rb_define_method(cSocket, "get_retransmission_info", rsctp_get_retransmission_info, 0);
2501
2862
  rb_define_method(cSocket, "get_status", rsctp_get_status, 0);
2502
2863
  rb_define_method(cSocket, "get_subscriptions", rsctp_get_subscriptions, 0);
2503
2864
  rb_define_method(cSocket, "listen", rsctp_listen, -1);
2504
2865
  rb_define_method(cSocket, "map_ipv4=", rsctp_map_ipv4, 1);
2866
+ rb_define_method(cSocket, "map_ipv4?", rsctp_get_map_ipv4, 0);
2505
2867
  rb_define_method(cSocket, "nodelay?", rsctp_get_nodelay, 0);
2506
2868
  rb_define_method(cSocket, "nodelay=", rsctp_set_nodelay, 1);
2507
2869
  rb_define_method(cSocket, "peeloff", rsctp_peeloff, 1);
@@ -2521,12 +2883,15 @@ void Init_socket(void){
2521
2883
  rb_define_method(cSocket, "set_association_info", rsctp_set_association_info, 1);
2522
2884
  rb_define_method(cSocket, "set_initmsg", rsctp_set_initmsg, 1);
2523
2885
  rb_define_method(cSocket, "set_retransmission_info", rsctp_set_retransmission_info, 1);
2886
+ rb_define_method(cSocket, "set_default_send_params", rsctp_set_default_send_params, 1);
2887
+ rb_define_method(cSocket, "set_peer_address_params", rsctp_set_peer_address_params, 1);
2524
2888
  rb_define_method(cSocket, "set_shared_key", rsctp_set_shared_key, -1);
2525
2889
  rb_define_method(cSocket, "shutdown", rsctp_shutdown, -1);
2526
2890
  rb_define_method(cSocket, "subscribe", rsctp_subscribe, 1);
2527
2891
 
2528
2892
  rb_define_alias(cSocket, "get_rto_info", "get_retransmission_info");
2529
2893
  rb_define_alias(cSocket, "set_rto_info", "set_retransmission_info");
2894
+ rb_define_alias(cSocket, "get_initmsg", "get_init_msg");
2530
2895
 
2531
2896
  rb_define_attr(cSocket, "domain", 1, 1);
2532
2897
  rb_define_attr(cSocket, "type", 1, 1);
@@ -2535,7 +2900,7 @@ void Init_socket(void){
2535
2900
  rb_define_attr(cSocket, "port", 1, 1);
2536
2901
 
2537
2902
  /* 0.1.4: The version of this library */
2538
- rb_define_const(cSocket, "VERSION", rb_str_new2("0.1.4"));
2903
+ rb_define_const(cSocket, "VERSION", rb_str_new2("0.2.0"));
2539
2904
 
2540
2905
  /* send flags */
2541
2906
 
@@ -2574,4 +2939,34 @@ void Init_socket(void){
2574
2939
 
2575
2940
  rb_define_const(cSocket, "SCTP_BINDX_ADD_ADDR", INT2NUM(SCTP_BINDX_ADD_ADDR));
2576
2941
  rb_define_const(cSocket, "SCTP_BINDX_REM_ADDR", INT2NUM(SCTP_BINDX_REM_ADDR));
2942
+
2943
+ // PEER ADDRESS PARAMETER FLAGS //
2944
+
2945
+ #ifdef SPP_HB_ENABLE
2946
+ rb_define_const(cSocket, "SPP_HB_ENABLE", INT2NUM(SPP_HB_ENABLE));
2947
+ #endif
2948
+ #ifdef SPP_HB_DISABLE
2949
+ rb_define_const(cSocket, "SPP_HB_DISABLE", INT2NUM(SPP_HB_DISABLE));
2950
+ #endif
2951
+ #ifdef SPP_HB_DEMAND
2952
+ rb_define_const(cSocket, "SPP_HB_DEMAND", INT2NUM(SPP_HB_DEMAND));
2953
+ #endif
2954
+ #ifdef SPP_PMTUD_ENABLE
2955
+ rb_define_const(cSocket, "SPP_PMTUD_ENABLE", INT2NUM(SPP_PMTUD_ENABLE));
2956
+ #endif
2957
+ #ifdef SPP_PMTUD_DISABLE
2958
+ rb_define_const(cSocket, "SPP_PMTUD_DISABLE", INT2NUM(SPP_PMTUD_DISABLE));
2959
+ #endif
2960
+
2961
+ // PARTIAL RELIABILITY SCTP POLICY CONSTANTS //
2962
+
2963
+ #ifdef SCTP_PR_SCTP_TTL
2964
+ rb_define_const(cSocket, "SCTP_PR_SCTP_TTL", INT2NUM(SCTP_PR_SCTP_TTL));
2965
+ #endif
2966
+ #ifdef SCTP_PR_SCTP_RTX
2967
+ rb_define_const(cSocket, "SCTP_PR_SCTP_RTX", INT2NUM(SCTP_PR_SCTP_RTX));
2968
+ #endif
2969
+ #ifdef SCTP_PR_SCTP_BUF
2970
+ rb_define_const(cSocket, "SCTP_PR_SCTP_BUF", INT2NUM(SCTP_PR_SCTP_BUF));
2971
+ #endif
2577
2972
  }