sctp-socket 0.1.4 → 0.2.1

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,13 @@ 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);
853
+
854
+ CHECK_SOCKET_CLOSED(self);
735
855
 
736
856
  if(!NIL_P(v_addresses)){
737
857
  Check_Type(v_addresses, T_ARRAY);
@@ -807,24 +927,48 @@ static VALUE rsctp_sendv(VALUE self, VALUE v_options){
807
927
  #endif
808
928
 
809
929
  #ifdef HAVE_SCTP_RECVV
930
+ /*
931
+ * call-seq:
932
+ * SCTP::Socket#recvv(flags=0, buffer_size=1024)
933
+ *
934
+ * Receive a message using sctp_recvv from another SCTP endpoint.
935
+ *
936
+ * The optional buffer_size parameter specifies the size of the receive buffer
937
+ * in bytes. Defaults to 1024 bytes if not specified.
938
+ *
939
+ * Example:
940
+ *
941
+ * begin
942
+ * socket = SCTP::Socket.new
943
+ * socket.bind(:port => 62534, :addresses => ['10.0.4.5', '10.0.5.5'])
944
+ * socket.listen
945
+ *
946
+ * while true
947
+ * info = socket.recvv
948
+ * puts "Received message: #{info.message}"
949
+ *
950
+ * # Or with custom buffer size
951
+ * info = socket.recvv(0, 4096)
952
+ * puts "Received message: #{info.message}"
953
+ * end
954
+ * ensure
955
+ * socket.close
956
+ * end
957
+ */
810
958
  static VALUE rsctp_recvv(int argc, VALUE* argv, VALUE self){
811
- VALUE v_flags;
812
- int fileno, flags, on;
959
+ VALUE v_flags, v_buffer_size;
960
+ int fileno, flags, on, buffer_size;
813
961
  ssize_t bytes;
814
962
  uint infotype;
815
963
  socklen_t infolen;
816
964
  struct iovec iov[1];
817
965
  struct sctp_rcvinfo info;
818
- char buffer[1024];
966
+ char *buffer;
819
967
 
820
968
  bzero(&iov, sizeof(iov));
821
969
  bzero(&info, sizeof(info));
822
- bzero(&buffer, sizeof(buffer));
823
-
824
- iov->iov_base = buffer;
825
- iov->iov_len = sizeof(buffer);
826
970
 
827
- rb_scan_args(argc, argv, "01", &v_flags);
971
+ rb_scan_args(argc, argv, "02", &v_flags, &v_buffer_size);
828
972
 
829
973
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
830
974
 
@@ -833,9 +977,28 @@ static VALUE rsctp_recvv(int argc, VALUE* argv, VALUE self){
833
977
  else
834
978
  flags = NUM2INT(v_flags);
835
979
 
980
+ if(NIL_P(v_buffer_size))
981
+ buffer_size = 1024;
982
+ else
983
+ buffer_size = NUM2INT(v_buffer_size);
984
+
985
+ if(buffer_size <= 0)
986
+ rb_raise(rb_eArgError, "buffer size must be positive");
987
+
988
+ buffer = (char*)malloc(buffer_size);
989
+ if(buffer == NULL)
990
+ rb_raise(rb_eNoMemError, "failed to allocate buffer");
991
+
992
+ bzero(buffer, buffer_size);
993
+
994
+ iov->iov_base = buffer;
995
+ iov->iov_len = buffer_size;
996
+
836
997
  on = 1;
837
- if(setsockopt(fileno, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(on)) < 0)
998
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(on)) < 0){
999
+ free(buffer);
838
1000
  rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
1001
+ }
839
1002
 
840
1003
  infolen = sizeof(struct sctp_rcvinfo);
841
1004
  infotype = 0;
@@ -852,16 +1015,19 @@ static VALUE rsctp_recvv(int argc, VALUE* argv, VALUE self){
852
1015
  &flags
853
1016
  );
854
1017
 
855
- if(bytes < 0)
1018
+ if(bytes < 0){
1019
+ free(buffer);
856
1020
  rb_raise(rb_eSystemCallError, "sctp_recvv: %s", strerror(errno));
1021
+ }
857
1022
 
858
1023
  if(infotype != SCTP_RECVV_RCVINFO){
1024
+ free(buffer);
859
1025
  return Qnil;
860
1026
  }
861
1027
  else{
862
- return rb_struct_new(
1028
+ VALUE result = rb_struct_new(
863
1029
  v_sctp_receive_info_struct,
864
- rb_str_new2(iov->iov_base),
1030
+ rb_str_new(iov->iov_base, bytes),
865
1031
  UINT2NUM(info.rcv_sid),
866
1032
  UINT2NUM(info.rcv_ssn),
867
1033
  UINT2NUM(info.rcv_flags),
@@ -871,6 +1037,8 @@ static VALUE rsctp_recvv(int argc, VALUE* argv, VALUE self){
871
1037
  UINT2NUM(info.rcv_context),
872
1038
  UINT2NUM(info.rcv_assoc_id)
873
1039
  );
1040
+ free(buffer);
1041
+ return result;
874
1042
  }
875
1043
  }
876
1044
  #endif
@@ -982,7 +1150,7 @@ static VALUE rsctp_send(VALUE self, VALUE v_options){
982
1150
  * :stream -> The SCTP stream number you wish to send the message on.
983
1151
  * :addresses -> An array of addresses to send the message to.
984
1152
  * :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.
1153
+ * :ppid -> The payload protocol identifier that is passed to the peer endpoint.
986
1154
  * :flags -> A bitwise integer that contain one or more values that control behavior.
987
1155
  *
988
1156
  * Note that the :addresses option is not mandatory in a one-to-one (SOCK_STREAM)
@@ -1008,7 +1176,7 @@ static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
1008
1176
  uint16_t stream;
1009
1177
  uint32_t ppid, flags, ttl, context;
1010
1178
  ssize_t num_bytes;
1011
- struct sockaddr_in addrs[8];
1179
+ struct sockaddr_in addrs[MAX_IP_ADDRESSES];
1012
1180
  int fileno, size, num_ip;
1013
1181
 
1014
1182
  Check_Type(v_options, T_HASH);
@@ -1023,6 +1191,9 @@ static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
1023
1191
  v_ttl = rb_hash_aref2(v_options, "ttl");
1024
1192
  v_addresses = rb_hash_aref2(v_options, "addresses");
1025
1193
 
1194
+ if(NIL_P(v_msg))
1195
+ rb_raise(rb_eArgError, "message parameter is required");
1196
+
1026
1197
  if(NIL_P(v_stream))
1027
1198
  stream = 0;
1028
1199
  else
@@ -1055,6 +1226,7 @@ static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
1055
1226
  int i, port;
1056
1227
  VALUE v_address, v_port;
1057
1228
 
1229
+ Check_Type(v_addresses, T_ARRAY);
1058
1230
  num_ip = (int)RARRAY_LEN(v_addresses);
1059
1231
  v_port = rb_hash_aref2(v_options, "port");
1060
1232
 
@@ -1142,10 +1314,13 @@ static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
1142
1314
 
1143
1315
  /*
1144
1316
  * call-seq:
1145
- * SCTP::Socket#recvmsg(flags=0)
1317
+ * SCTP::Socket#recvmsg(flags=0, buffer_size=1024)
1146
1318
  *
1147
1319
  * Receive a message from another SCTP endpoint.
1148
1320
  *
1321
+ * The optional buffer_size parameter specifies the size of the receive buffer
1322
+ * in bytes. Defaults to 1024 bytes if not specified.
1323
+ *
1149
1324
  * Example:
1150
1325
  *
1151
1326
  * begin
@@ -1157,46 +1332,64 @@ static VALUE rsctp_sendmsg(VALUE self, VALUE v_options){
1157
1332
  * while true
1158
1333
  * info = socket.recvmsg
1159
1334
  * puts "Received message: #{info.message}"
1335
+ *
1336
+ * # Or with custom buffer size
1337
+ * info = socket.recvmsg(0, 4096)
1338
+ * puts "Received message: #{info.message}"
1160
1339
  * end
1161
1340
  * ensure
1162
1341
  * socket.close
1163
1342
  * end
1164
1343
  */
1165
1344
  static VALUE rsctp_recvmsg(int argc, VALUE* argv, VALUE self){
1166
- VALUE v_flags, v_notification, v_message;
1345
+ VALUE v_flags, v_buffer_size, v_notification, v_message;
1167
1346
  struct sctp_sndrcvinfo sndrcvinfo;
1168
1347
  struct sockaddr_in clientaddr;
1169
- int flags, fileno;
1348
+ int flags, fileno, buffer_size;
1170
1349
  ssize_t bytes;
1171
- char buffer[1024]; // TODO: Let this be configurable?
1350
+ char *buffer;
1172
1351
  socklen_t length;
1173
1352
 
1174
- rb_scan_args(argc, argv, "01", &v_flags);
1353
+ rb_scan_args(argc, argv, "02", &v_flags, &v_buffer_size);
1175
1354
 
1176
1355
  if(NIL_P(v_flags))
1177
1356
  flags = 0;
1178
1357
  else
1179
- flags = NUM2INT(v_flags);
1358
+ flags = NUM2INT(v_flags);
1359
+
1360
+ if(NIL_P(v_buffer_size))
1361
+ buffer_size = 1024;
1362
+ else
1363
+ buffer_size = NUM2INT(v_buffer_size);
1364
+
1365
+ if(buffer_size <= 0)
1366
+ rb_raise(rb_eArgError, "buffer size must be positive");
1367
+
1368
+ buffer = (char*)malloc(buffer_size);
1369
+ if(buffer == NULL)
1370
+ rb_raise(rb_eNoMemError, "failed to allocate buffer");
1180
1371
 
1181
1372
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1182
1373
  length = sizeof(struct sockaddr_in);
1183
1374
 
1184
- bzero(buffer, sizeof(buffer));
1375
+ bzero(buffer, buffer_size);
1185
1376
  bzero(&clientaddr, sizeof(clientaddr));
1186
1377
  bzero(&sndrcvinfo, sizeof(sndrcvinfo));
1187
1378
 
1188
1379
  bytes = (ssize_t)sctp_recvmsg(
1189
1380
  fileno,
1190
1381
  buffer,
1191
- sizeof(buffer),
1382
+ buffer_size,
1192
1383
  (struct sockaddr*)&clientaddr,
1193
1384
  &length,
1194
1385
  &sndrcvinfo,
1195
1386
  &flags
1196
1387
  );
1197
1388
 
1198
- if(bytes < 0)
1389
+ if(bytes < 0){
1390
+ free(buffer);
1199
1391
  rb_raise(rb_eSystemCallError, "sctp_recvmsg: %s", strerror(errno));
1392
+ }
1200
1393
 
1201
1394
  v_notification = Qnil;
1202
1395
 
@@ -1208,6 +1401,8 @@ static VALUE rsctp_recvmsg(int argc, VALUE* argv, VALUE self){
1208
1401
  else
1209
1402
  v_message = Qnil;
1210
1403
 
1404
+ free(buffer);
1405
+
1211
1406
  return rb_struct_new(v_sndrcv_struct,
1212
1407
  v_message,
1213
1408
  UINT2NUM(sndrcvinfo.sinfo_stream),
@@ -1305,7 +1500,7 @@ static VALUE rsctp_set_initmsg(VALUE self, VALUE v_options){
1305
1500
  * :peer_error (aka remote error)
1306
1501
  *
1307
1502
  * Example:
1308
- *
1503
+ *
1309
1504
  * socket = SCTP::Socket.new
1310
1505
  *
1311
1506
  * socket.bind(:port => port, :addresses => ['127.0.0.1'])
@@ -1402,7 +1597,7 @@ static VALUE rsctp_listen(int argc, VALUE* argv, VALUE self){
1402
1597
 
1403
1598
  if(listen(fileno, backlog) < 0)
1404
1599
  rb_raise(rb_eSystemCallError, "listen: %s", strerror(errno));
1405
-
1600
+
1406
1601
  return self;
1407
1602
  }
1408
1603
 
@@ -1427,7 +1622,7 @@ static VALUE rsctp_listen(int argc, VALUE* argv, VALUE self){
1427
1622
  static VALUE rsctp_peeloff(VALUE self, VALUE v_assoc_id){
1428
1623
  int fileno, assoc_fileno;
1429
1624
  sctp_assoc_t assoc_id;
1430
-
1625
+
1431
1626
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1432
1627
  assoc_id = NUM2INT(v_assoc_id);
1433
1628
 
@@ -1884,7 +2079,7 @@ static VALUE rsctp_get_subscriptions(VALUE self){
1884
2079
  */
1885
2080
  static VALUE rsctp_get_peer_address_params(VALUE self){
1886
2081
  int fileno;
1887
- char str[16];
2082
+ char str[IP_BUFFER_SIZE];
1888
2083
  socklen_t size;
1889
2084
  sctp_assoc_t assoc_id;
1890
2085
  struct sctp_paddrparams paddr;
@@ -1991,11 +2186,9 @@ static VALUE rsctp_get_nodelay(VALUE self){
1991
2186
  static VALUE rsctp_set_nodelay(VALUE self, VALUE v_bool){
1992
2187
  int fileno;
1993
2188
  socklen_t size;
1994
- sctp_assoc_t assoc_id;
1995
2189
  int value;
1996
2190
 
1997
2191
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
1998
- assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
1999
2192
  size = sizeof(int);
2000
2193
 
2001
2194
  if(NIL_P(v_bool) || v_bool == Qfalse)
@@ -2003,8 +2196,8 @@ static VALUE rsctp_set_nodelay(VALUE self, VALUE v_bool){
2003
2196
  else
2004
2197
  value = 1;
2005
2198
 
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));
2199
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_NODELAY, &value, size) < 0)
2200
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2008
2201
 
2009
2202
  if(value)
2010
2203
  return Qtrue;
@@ -2015,7 +2208,7 @@ static VALUE rsctp_set_nodelay(VALUE self, VALUE v_bool){
2015
2208
  /*
2016
2209
  * call-seq:
2017
2210
  * SCTP::Socket#disable_fragments=(bool)
2018
- *
2211
+ *
2019
2212
  * This option is a on/off flag and is passed an integer where a non-
2020
2213
  * zero is on and a zero is off. If enabled no SCTP message
2021
2214
  * fragmentation will be performed. Instead if a message being sent
@@ -2093,19 +2286,15 @@ static VALUE rsctp_get_autoclose(VALUE self){
2093
2286
  */
2094
2287
  static VALUE rsctp_set_autoclose(VALUE self, VALUE v_seconds){
2095
2288
  int fileno;
2096
- socklen_t size;
2097
- sctp_assoc_t assoc_id;
2098
2289
  int value;
2099
2290
 
2100
2291
  value = NUM2INT(v_seconds);
2101
2292
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2102
- assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
2103
- size = sizeof(int);
2104
2293
 
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));
2294
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_AUTOCLOSE, &value, sizeof(value)) < 0)
2295
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2107
2296
 
2108
- return v_seconds;
2297
+ return INT2NUM(value);
2109
2298
  }
2110
2299
 
2111
2300
  /*
@@ -2123,6 +2312,8 @@ static VALUE rsctp_enable_auth_support(int argc, VALUE* argv, VALUE self){
2123
2312
 
2124
2313
  rb_scan_args(argc, argv, "01", &v_assoc_id);
2125
2314
 
2315
+ CHECK_SOCKET_CLOSED(self);
2316
+
2126
2317
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2127
2318
  size = sizeof(struct sctp_assoc_value);
2128
2319
 
@@ -2134,15 +2325,52 @@ static VALUE rsctp_enable_auth_support(int argc, VALUE* argv, VALUE self){
2134
2325
  assoc_value.assoc_id = assoc_id;
2135
2326
  assoc_value.assoc_value = 1;
2136
2327
 
2328
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_AUTH_SUPPORTED, (void*)&assoc_value, size) < 0)
2329
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2330
+
2331
+ return self;
2332
+ }
2333
+
2334
+ /*
2335
+ * call-seq:
2336
+ * SCTP::Socket#auth_support?(association_id=nil)
2337
+ *
2338
+ * Returns whether or not authentication support is enabled for the association.
2339
+ * Returns true if auth support is enabled, false otherwise.
2340
+ */
2341
+ static VALUE rsctp_get_auth_support(int argc, VALUE* argv, VALUE self){
2342
+ int fileno;
2343
+ socklen_t size;
2344
+ sctp_assoc_t assoc_id;
2345
+ struct sctp_assoc_value assoc_value;
2346
+ VALUE v_assoc_id;
2347
+
2348
+ rb_scan_args(argc, argv, "01", &v_assoc_id);
2349
+
2350
+ CHECK_SOCKET_CLOSED(self);
2351
+
2352
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2353
+ size = sizeof(struct sctp_assoc_value);
2354
+
2355
+ if(NIL_P(v_assoc_id))
2356
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
2357
+ else
2358
+ assoc_id = NUM2INT(v_assoc_id);
2359
+
2360
+ assoc_value.assoc_id = assoc_id;
2361
+
2137
2362
  if(sctp_opt_info(fileno, assoc_id, SCTP_AUTH_SUPPORTED, (void*)&assoc_value, &size) < 0)
2138
2363
  rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
2139
2364
 
2140
- return self;
2365
+ if(assoc_value.assoc_value)
2366
+ return Qtrue;
2367
+ else
2368
+ return Qfalse;
2141
2369
  }
2142
2370
 
2143
2371
  /*
2144
2372
  * call-seq:
2145
- * SCTP::Socket#set_shared_key(key, keynum, association_id=nil)
2373
+ * SCTP::Socket#set_shared_key(key, keynum=1, association_id=nil)
2146
2374
  *
2147
2375
  * This option will set a shared secret key which is used to build an
2148
2376
  * association shared key.
@@ -2178,18 +2406,11 @@ static VALUE rsctp_set_shared_key(int argc, VALUE* argv, VALUE self){
2178
2406
 
2179
2407
  rb_scan_args(argc, argv, "12", &v_key, &v_keynumber, &v_assoc_id);
2180
2408
 
2409
+ CHECK_SOCKET_CLOSED(self);
2410
+
2181
2411
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2182
2412
  key = StringValuePtr(v_key);
2183
- len = strlen(key);
2184
- unsigned char byte_array[len+1];
2185
-
2186
- for(size_t i = 0; i < len; i++)
2187
- byte_array[i] = key[i];
2188
-
2189
- byte_array[len] = '\0';
2190
-
2191
- auth_key = malloc(sizeof(auth_key) + sizeof(char[strlen(key)+1]));
2192
- size = sizeof(auth_key);
2413
+ len = RSTRING_LEN(v_key); // Use Ruby's string length, not strlen
2193
2414
 
2194
2415
  if(NIL_P(v_assoc_id))
2195
2416
  assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
@@ -2201,14 +2422,25 @@ static VALUE rsctp_set_shared_key(int argc, VALUE* argv, VALUE self){
2201
2422
  else
2202
2423
  keynum = NUM2INT(v_keynumber);
2203
2424
 
2425
+ // Allocate the structure with space for the key
2426
+ size = sizeof(struct sctp_authkey) + len;
2427
+ auth_key = malloc(size);
2428
+
2429
+ if (auth_key == NULL)
2430
+ rb_raise(rb_eNoMemError, "Failed to allocate memory for auth key");
2431
+
2204
2432
  auth_key->sca_assoc_id = assoc_id;
2205
2433
  auth_key->sca_keynumber = keynum;
2206
- auth_key->sca_keylength = strlen(key);
2207
- memcpy(auth_key->sca_key, byte_array, sizeof(byte_array));
2434
+ auth_key->sca_keylength = len;
2435
+ memcpy(auth_key->sca_key, key, len);
2208
2436
 
2209
- if(sctp_opt_info(fileno, assoc_id, SCTP_AUTH_KEY, (void*)auth_key, &size) < 0)
2210
- rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
2437
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_AUTH_KEY, (void*)auth_key, size) < 0) {
2438
+ int err = errno;
2439
+ free(auth_key);
2440
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(err));
2441
+ }
2211
2442
 
2443
+ free(auth_key);
2212
2444
  return self;
2213
2445
  }
2214
2446
 
@@ -2224,14 +2456,19 @@ static VALUE rsctp_get_active_shared_key(int argc, VALUE* argv, VALUE self){
2224
2456
  struct sctp_authkeyid authkey;
2225
2457
  sctp_assoc_t assoc_id;
2226
2458
  VALUE v_assoc_id, v_keynum;
2227
- uint keynum;
2459
+ int keynum;
2228
2460
 
2229
2461
  rb_scan_args(argc, argv, "11", &v_keynum, &v_assoc_id);
2230
2462
 
2231
2463
  bzero(&authkey, sizeof(authkey));
2232
2464
 
2465
+ // Cast it later, we want to force a validity check.
2466
+ keynum = FIX2INT(v_keynum);
2467
+
2468
+ if(keynum < 0)
2469
+ rb_raise(rb_eArgError, "invalid keynum value");
2470
+
2233
2471
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2234
- keynum = NUM2UINT(v_keynum);
2235
2472
 
2236
2473
  if(NIL_P(v_assoc_id))
2237
2474
  assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
@@ -2239,7 +2476,7 @@ static VALUE rsctp_get_active_shared_key(int argc, VALUE* argv, VALUE self){
2239
2476
  assoc_id = NUM2INT(v_assoc_id);
2240
2477
 
2241
2478
  authkey.scact_assoc_id = assoc_id;
2242
- authkey.scact_keynumber = keynum;
2479
+ authkey.scact_keynumber = (uint)keynum;
2243
2480
 
2244
2481
  size = sizeof(struct sctp_authkeyid);
2245
2482
 
@@ -2260,7 +2497,7 @@ static VALUE rsctp_get_active_shared_key(int argc, VALUE* argv, VALUE self){
2260
2497
  * authenticated chunks. The key identifier MUST correspond to an existing
2261
2498
  * shared key. Note that shared key identifier '0' defaults to a null key.
2262
2499
  *
2263
- * The association_idparameter, if non-zero, indicates what association that
2500
+ * The association_id parameter, if non-zero, indicates what association that
2264
2501
  * the shared key identifier is being set active upon. If this element contains
2265
2502
  * zero, then the activation applies to the endpoint and all future
2266
2503
  * associations will use the specified shared key identifier.
@@ -2277,11 +2514,15 @@ static VALUE rsctp_set_active_shared_key(int argc, VALUE* argv, VALUE self){
2277
2514
  struct sctp_authkeyid authkey;
2278
2515
  sctp_assoc_t assoc_id;
2279
2516
  VALUE v_assoc_id, v_keynum;
2280
- uint keynum;
2517
+ int keynum;
2281
2518
 
2282
2519
  rb_scan_args(argc, argv, "11", &v_keynum, &v_assoc_id);
2283
2520
 
2284
- keynum = NUM2UINT(v_keynum);
2521
+ keynum = FIX2INT(v_keynum);
2522
+
2523
+ if(keynum < 0)
2524
+ rb_raise(rb_eArgError, "invalid keynum value");
2525
+
2285
2526
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2286
2527
 
2287
2528
  if(NIL_P(v_assoc_id))
@@ -2290,11 +2531,11 @@ static VALUE rsctp_set_active_shared_key(int argc, VALUE* argv, VALUE self){
2290
2531
  assoc_id = NUM2INT(v_assoc_id);
2291
2532
 
2292
2533
  authkey.scact_assoc_id = assoc_id;
2293
- authkey.scact_keynumber = keynum;
2534
+ authkey.scact_keynumber = (uint)keynum;
2294
2535
  size = sizeof(struct sctp_authkeyid);
2295
2536
 
2296
- if(sctp_opt_info(fileno, assoc_id, SCTP_AUTH_ACTIVE_KEY, (void*)&authkey, &size) < 0)
2297
- rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
2537
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY, (void*)&authkey, size) < 0)
2538
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2298
2539
 
2299
2540
  return self;
2300
2541
  }
@@ -2334,6 +2575,8 @@ static VALUE rsctp_delete_shared_key(int argc, VALUE* argv, VALUE self){
2334
2575
 
2335
2576
  rb_scan_args(argc, argv, "11", &v_keynum, &v_assoc_id);
2336
2577
 
2578
+ CHECK_SOCKET_CLOSED(self);
2579
+
2337
2580
  bzero(&authkey, sizeof(authkey));
2338
2581
 
2339
2582
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
@@ -2349,8 +2592,8 @@ static VALUE rsctp_delete_shared_key(int argc, VALUE* argv, VALUE self){
2349
2592
 
2350
2593
  size = sizeof(struct sctp_authkeyid);
2351
2594
 
2352
- if(sctp_opt_info(fileno, assoc_id, SCTP_AUTH_DELETE_KEY, (void*)&authkey, &size) < 0)
2353
- rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
2595
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_AUTH_DELETE_KEY, (void*)&authkey, size) < 0)
2596
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2354
2597
 
2355
2598
  return INT2NUM(authkey.scact_keynumber);
2356
2599
  }
@@ -2359,29 +2602,239 @@ static VALUE rsctp_delete_shared_key(int argc, VALUE* argv, VALUE self){
2359
2602
  * call-seq:
2360
2603
  * SCTP::Socket#map_ipv4=(bool)
2361
2604
  *
2362
- * If set to true and the socket is type PF_INET6, then IPv4 addresses will be
2605
+ * If set to true and the socket is type xF_INET6, then IPv4 addresses will be
2363
2606
  * mapped to V6 representation. If set to false (the default), then no mapping
2364
- * will be done of V4 addresses and a user will receive both PF_INET6 and
2365
- * PF_INET type addresses on the socket.
2607
+ * will be done of V4 addresses and a user will receive both xF_INET6 and
2608
+ * xF_INET type addresses on the socket.
2609
+ *
2610
+ * Note that setting this to true on a socket that is not type xF_INET6 is
2611
+ * undefined behavior, and may raise an error on your platform. Otherwise it's
2612
+ * a no-op.
2366
2613
  */
2367
2614
  static VALUE rsctp_map_ipv4(VALUE self, VALUE v_bool){
2368
2615
  int fileno, boolean;
2369
- sctp_assoc_t assoc_id;
2370
- socklen_t size;
2371
2616
 
2372
2617
  boolean = 0;
2373
2618
  fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2374
- assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
2375
2619
 
2376
2620
  if(v_bool == Qtrue)
2377
2621
  boolean = 1;
2378
2622
 
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));
2623
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, (void*)&boolean, sizeof(boolean)) < 0)
2624
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2381
2625
 
2382
2626
  return v_bool;
2383
2627
  }
2384
2628
 
2629
+ /*
2630
+ * call-seq:
2631
+ * SCTP::Socket#map_ipv4?
2632
+ *
2633
+ * Returns whether or not IPv4 addresses will be mapped to V6 representation
2634
+ * for PF_INET6 sockets. Returns true if mapping is enabled, false otherwise.
2635
+ */
2636
+ static VALUE rsctp_get_map_ipv4(VALUE self){
2637
+ int fileno;
2638
+ socklen_t size;
2639
+ sctp_assoc_t assoc_id;
2640
+ int value;
2641
+
2642
+ CHECK_SOCKET_CLOSED(self);
2643
+
2644
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2645
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
2646
+ size = sizeof(int);
2647
+
2648
+ if(sctp_opt_info(fileno, assoc_id, SCTP_I_WANT_MAPPED_V4_ADDR, (void*)&value, &size) < 0)
2649
+ rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
2650
+
2651
+ if(value)
2652
+ return Qtrue;
2653
+ else
2654
+ return Qfalse;
2655
+ }
2656
+
2657
+ /*
2658
+ * call-seq:
2659
+ * SCTP::Socket#set_default_send_params(options)
2660
+ *
2661
+ * Sets the default parameters that will be used by the sendmsg() call
2662
+ * when it is invoked with a null parameter.
2663
+ *
2664
+ * The +options+ hash may contain the following keys:
2665
+ *
2666
+ * * stream: A number indicating the stream number within the association
2667
+ * * ssn: Not used by applications. Ignored for one-to-many style sockets.
2668
+ * * flags: Indicates various options for sending. See SCTP_* constants.
2669
+ * * ppid: The payload protocol identifier
2670
+ * * context: User-specified context information
2671
+ * * ttl: The time to live (in milliseconds)
2672
+ * * tsn: Not used by applications
2673
+ * * cumtsn: Not used by applications
2674
+ * * association_id: The association identification (ignored for one-to-one sockets)
2675
+ *
2676
+ * Example:
2677
+ *
2678
+ * socket.set_default_send_params(:stream => 1, :flags => SCTP::Socket::SCTP_UNORDERED)
2679
+ */
2680
+ static VALUE rsctp_set_default_send_params(VALUE self, VALUE v_options){
2681
+ VALUE v_stream, v_ssn, v_flags, v_ppid, v_context, v_ttl, v_tsn, v_cumtsn, v_assoc_id;
2682
+ int fileno;
2683
+ sctp_assoc_t assoc_id;
2684
+ struct sctp_sndrcvinfo sndrcv;
2685
+
2686
+ if(!RB_TYPE_P(v_options, T_HASH))
2687
+ rb_raise(rb_eTypeError, "options must be a hash");
2688
+
2689
+ bzero(&sndrcv, sizeof(sndrcv));
2690
+
2691
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2692
+
2693
+ v_stream = rb_hash_aref2(v_options, "stream");
2694
+ v_ssn = rb_hash_aref2(v_options, "ssn");
2695
+ v_flags = rb_hash_aref2(v_options, "flags");
2696
+ v_ppid = rb_hash_aref2(v_options, "ppid");
2697
+ v_context = rb_hash_aref2(v_options, "context");
2698
+ v_ttl = rb_hash_aref2(v_options, "ttl");
2699
+ v_tsn = rb_hash_aref2(v_options, "tsn");
2700
+ v_cumtsn = rb_hash_aref2(v_options, "cumtsn");
2701
+ v_assoc_id = rb_hash_aref2(v_options, "association_id");
2702
+
2703
+ if(NIL_P(v_assoc_id))
2704
+ v_assoc_id = rb_iv_get(self, "@association_id");
2705
+
2706
+ assoc_id = NUM2INT(v_assoc_id);
2707
+ sndrcv.sinfo_assoc_id = assoc_id;
2708
+
2709
+ if(!NIL_P(v_stream))
2710
+ sndrcv.sinfo_stream = NUM2INT(v_stream);
2711
+
2712
+ if(!NIL_P(v_ssn))
2713
+ sndrcv.sinfo_ssn = NUM2INT(v_ssn);
2714
+
2715
+ if(!NIL_P(v_flags))
2716
+ sndrcv.sinfo_flags = NUM2INT(v_flags);
2717
+
2718
+ if(!NIL_P(v_ppid))
2719
+ sndrcv.sinfo_ppid = NUM2INT(v_ppid);
2720
+
2721
+ if(!NIL_P(v_context))
2722
+ sndrcv.sinfo_context = NUM2INT(v_context);
2723
+
2724
+ if(!NIL_P(v_ttl))
2725
+ sndrcv.sinfo_timetolive = NUM2INT(v_ttl);
2726
+
2727
+ if(!NIL_P(v_tsn))
2728
+ sndrcv.sinfo_tsn = NUM2INT(v_tsn);
2729
+
2730
+ if(!NIL_P(v_cumtsn))
2731
+ sndrcv.sinfo_cumtsn = NUM2INT(v_cumtsn);
2732
+
2733
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_DEFAULT_SEND_PARAM, &sndrcv, sizeof(sndrcv)) < 0)
2734
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2735
+
2736
+ return rb_struct_new(
2737
+ v_sctp_default_send_params_struct,
2738
+ v_stream,
2739
+ v_ssn,
2740
+ v_flags,
2741
+ v_ppid,
2742
+ v_context,
2743
+ v_ttl,
2744
+ v_tsn,
2745
+ v_cumtsn,
2746
+ v_assoc_id
2747
+ );
2748
+ }
2749
+
2750
+ /*
2751
+ * call-seq:
2752
+ * SCTP::Socket#set_peer_address_params(options)
2753
+ *
2754
+ * Sets the peer address parameters. This allows applications to enable or
2755
+ * disable heartbeats for any peer address of an association, set the maximum
2756
+ * number of retransmissions to a destination address, and set the path MTU.
2757
+ *
2758
+ * The +options+ hash may contain the following keys:
2759
+ *
2760
+ * * association_id: The association identification
2761
+ * * address: The address of the remote peer
2762
+ * * hbinterval: The heartbeat interval (in milliseconds)
2763
+ * * pathmaxrxt: The maximum number of retransmissions
2764
+ * * pathmtu: The path MTU
2765
+ * * flags: Flags to control heartbeats, etc.
2766
+ * * ipv6_flowlabel: IPv6 flow label (only for IPv6 addresses)
2767
+ *
2768
+ * Example:
2769
+ *
2770
+ * socket.set_peer_address_params(:hbinterval => 5000, :pathmaxrxt => 5)
2771
+ */
2772
+ static VALUE rsctp_set_peer_address_params(VALUE self, VALUE v_options){
2773
+ VALUE v_assoc_id, v_address, v_hbinterval, v_pathmaxrxt, v_pathmtu, v_flags, v_ipv6_flowlabel;
2774
+ int fileno;
2775
+ sctp_assoc_t assoc_id;
2776
+ struct sctp_paddrparams paddr;
2777
+ struct sockaddr_in* sin;
2778
+
2779
+ if(!RB_TYPE_P(v_options, T_HASH))
2780
+ rb_raise(rb_eTypeError, "options must be a hash");
2781
+
2782
+ bzero(&paddr, sizeof(paddr));
2783
+
2784
+ fileno = NUM2INT(rb_iv_get(self, "@fileno"));
2785
+
2786
+ v_assoc_id = rb_hash_aref2(v_options, "association_id");
2787
+ v_address = rb_hash_aref2(v_options, "address");
2788
+ v_hbinterval = rb_hash_aref2(v_options, "hbinterval");
2789
+ v_pathmaxrxt = rb_hash_aref2(v_options, "pathmaxrxt");
2790
+ v_pathmtu = rb_hash_aref2(v_options, "pathmtu");
2791
+ v_flags = rb_hash_aref2(v_options, "flags");
2792
+ v_ipv6_flowlabel = rb_hash_aref2(v_options, "ipv6_flowlabel");
2793
+
2794
+ if(NIL_P(v_assoc_id))
2795
+ v_assoc_id = rb_iv_get(self, "@association_id");
2796
+
2797
+ assoc_id = NUM2INT(v_assoc_id);
2798
+ paddr.spp_assoc_id = assoc_id;
2799
+
2800
+ // If address is provided, set up the sockaddr structure
2801
+ if(!NIL_P(v_address)){
2802
+ sin = (struct sockaddr_in*)&paddr.spp_address;
2803
+ sin->sin_family = AF_INET;
2804
+ if(inet_pton(AF_INET, StringValueCStr(v_address), &sin->sin_addr) <= 0)
2805
+ rb_raise(rb_eArgError, "invalid IP address");
2806
+ }
2807
+
2808
+ if(!NIL_P(v_hbinterval))
2809
+ paddr.spp_hbinterval = NUM2INT(v_hbinterval);
2810
+
2811
+ if(!NIL_P(v_pathmaxrxt))
2812
+ paddr.spp_pathmaxrxt = NUM2INT(v_pathmaxrxt);
2813
+
2814
+ if(!NIL_P(v_pathmtu))
2815
+ paddr.spp_pathmtu = NUM2INT(v_pathmtu);
2816
+
2817
+ if(!NIL_P(v_flags))
2818
+ paddr.spp_flags = NUM2INT(v_flags);
2819
+
2820
+ if(!NIL_P(v_ipv6_flowlabel))
2821
+ paddr.spp_ipv6_flowlabel = NUM2INT(v_ipv6_flowlabel);
2822
+
2823
+ if(setsockopt(fileno, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &paddr, sizeof(paddr)) < 0)
2824
+ rb_raise(rb_eSystemCallError, "setsockopt: %s", strerror(errno));
2825
+
2826
+ return rb_struct_new(
2827
+ v_sctp_peer_addr_params_struct,
2828
+ v_assoc_id,
2829
+ v_address,
2830
+ v_hbinterval,
2831
+ v_pathmaxrxt,
2832
+ v_pathmtu,
2833
+ v_flags,
2834
+ v_ipv6_flowlabel
2835
+ );
2836
+ }
2837
+
2385
2838
  void Init_socket(void){
2386
2839
  mSCTP = rb_define_module("SCTP");
2387
2840
  cSocket = rb_define_class_under(mSCTP, "Socket", rb_cObject);
@@ -2485,23 +2938,26 @@ void Init_socket(void){
2485
2938
  rb_define_method(cSocket, "autoclose=", rsctp_set_autoclose, 1);
2486
2939
  rb_define_method(cSocket, "bindx", rsctp_bindx, -1);
2487
2940
  rb_define_method(cSocket, "close", rsctp_close, -1);
2941
+ rb_define_method(cSocket, "closed?", rsctp_closed_p, 0);
2488
2942
  rb_define_method(cSocket, "connectx", rsctp_connectx, -1);
2489
2943
  rb_define_method(cSocket, "delete_shared_key", rsctp_delete_shared_key, -1);
2490
2944
  rb_define_method(cSocket, "disable_fragments=", rsctp_disable_fragments, 1);
2491
2945
  rb_define_method(cSocket, "enable_auth_support", rsctp_enable_auth_support, -1);
2946
+ rb_define_method(cSocket, "auth_support?", rsctp_get_auth_support, -1);
2492
2947
  rb_define_method(cSocket, "getpeernames", rsctp_getpeernames, -1);
2493
2948
  rb_define_method(cSocket, "getlocalnames", rsctp_getlocalnames, -1);
2494
2949
  rb_define_method(cSocket, "get_active_shared_key", rsctp_get_active_shared_key, -1);
2495
2950
  rb_define_method(cSocket, "get_association_info", rsctp_get_association_info, 0);
2496
2951
  rb_define_method(cSocket, "get_autoclose", rsctp_get_autoclose, 0);
2497
2952
  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);
2953
+ rb_define_method(cSocket, "get_init_msg", rsctp_get_init_msg, 0);
2499
2954
  rb_define_method(cSocket, "get_peer_address_params", rsctp_get_peer_address_params, 0);
2500
2955
  rb_define_method(cSocket, "get_retransmission_info", rsctp_get_retransmission_info, 0);
2501
2956
  rb_define_method(cSocket, "get_status", rsctp_get_status, 0);
2502
2957
  rb_define_method(cSocket, "get_subscriptions", rsctp_get_subscriptions, 0);
2503
2958
  rb_define_method(cSocket, "listen", rsctp_listen, -1);
2504
2959
  rb_define_method(cSocket, "map_ipv4=", rsctp_map_ipv4, 1);
2960
+ rb_define_method(cSocket, "map_ipv4?", rsctp_get_map_ipv4, 0);
2505
2961
  rb_define_method(cSocket, "nodelay?", rsctp_get_nodelay, 0);
2506
2962
  rb_define_method(cSocket, "nodelay=", rsctp_set_nodelay, 1);
2507
2963
  rb_define_method(cSocket, "peeloff", rsctp_peeloff, 1);
@@ -2521,12 +2977,15 @@ void Init_socket(void){
2521
2977
  rb_define_method(cSocket, "set_association_info", rsctp_set_association_info, 1);
2522
2978
  rb_define_method(cSocket, "set_initmsg", rsctp_set_initmsg, 1);
2523
2979
  rb_define_method(cSocket, "set_retransmission_info", rsctp_set_retransmission_info, 1);
2980
+ rb_define_method(cSocket, "set_default_send_params", rsctp_set_default_send_params, 1);
2981
+ rb_define_method(cSocket, "set_peer_address_params", rsctp_set_peer_address_params, 1);
2524
2982
  rb_define_method(cSocket, "set_shared_key", rsctp_set_shared_key, -1);
2525
2983
  rb_define_method(cSocket, "shutdown", rsctp_shutdown, -1);
2526
2984
  rb_define_method(cSocket, "subscribe", rsctp_subscribe, 1);
2527
2985
 
2528
2986
  rb_define_alias(cSocket, "get_rto_info", "get_retransmission_info");
2529
2987
  rb_define_alias(cSocket, "set_rto_info", "set_retransmission_info");
2988
+ rb_define_alias(cSocket, "get_initmsg", "get_init_msg");
2530
2989
 
2531
2990
  rb_define_attr(cSocket, "domain", 1, 1);
2532
2991
  rb_define_attr(cSocket, "type", 1, 1);
@@ -2534,8 +2993,8 @@ void Init_socket(void){
2534
2993
  rb_define_attr(cSocket, "association_id", 1, 1);
2535
2994
  rb_define_attr(cSocket, "port", 1, 1);
2536
2995
 
2537
- /* 0.1.4: The version of this library */
2538
- rb_define_const(cSocket, "VERSION", rb_str_new2("0.1.4"));
2996
+ /* 0.2.0: The version of this library */
2997
+ rb_define_const(cSocket, "VERSION", rb_str_new2("0.2.1"));
2539
2998
 
2540
2999
  /* send flags */
2541
3000
 
@@ -2574,4 +3033,34 @@ void Init_socket(void){
2574
3033
 
2575
3034
  rb_define_const(cSocket, "SCTP_BINDX_ADD_ADDR", INT2NUM(SCTP_BINDX_ADD_ADDR));
2576
3035
  rb_define_const(cSocket, "SCTP_BINDX_REM_ADDR", INT2NUM(SCTP_BINDX_REM_ADDR));
3036
+
3037
+ // PEER ADDRESS PARAMETER FLAGS //
3038
+
3039
+ #ifdef SPP_HB_ENABLE
3040
+ rb_define_const(cSocket, "SPP_HB_ENABLE", INT2NUM(SPP_HB_ENABLE));
3041
+ #endif
3042
+ #ifdef SPP_HB_DISABLE
3043
+ rb_define_const(cSocket, "SPP_HB_DISABLE", INT2NUM(SPP_HB_DISABLE));
3044
+ #endif
3045
+ #ifdef SPP_HB_DEMAND
3046
+ rb_define_const(cSocket, "SPP_HB_DEMAND", INT2NUM(SPP_HB_DEMAND));
3047
+ #endif
3048
+ #ifdef SPP_PMTUD_ENABLE
3049
+ rb_define_const(cSocket, "SPP_PMTUD_ENABLE", INT2NUM(SPP_PMTUD_ENABLE));
3050
+ #endif
3051
+ #ifdef SPP_PMTUD_DISABLE
3052
+ rb_define_const(cSocket, "SPP_PMTUD_DISABLE", INT2NUM(SPP_PMTUD_DISABLE));
3053
+ #endif
3054
+
3055
+ // PARTIAL RELIABILITY SCTP POLICY CONSTANTS //
3056
+
3057
+ #ifdef SCTP_PR_SCTP_TTL
3058
+ rb_define_const(cSocket, "SCTP_PR_SCTP_TTL", INT2NUM(SCTP_PR_SCTP_TTL));
3059
+ #endif
3060
+ #ifdef SCTP_PR_SCTP_RTX
3061
+ rb_define_const(cSocket, "SCTP_PR_SCTP_RTX", INT2NUM(SCTP_PR_SCTP_RTX));
3062
+ #endif
3063
+ #ifdef SCTP_PR_SCTP_BUF
3064
+ rb_define_const(cSocket, "SCTP_PR_SCTP_BUF", INT2NUM(SCTP_PR_SCTP_BUF));
3065
+ #endif
2577
3066
  }