ext/sctp/socket.c in sctp-socket-0.0.4 vs ext/sctp/socket.c in sctp-socket-0.0.5
- old
+ new
@@ -15,15 +15,48 @@
VALUE v_sndinfo_struct;
VALUE v_adaptation_event_struct;
VALUE v_partial_delivery_event_struct;
VALUE v_auth_event_struct;
VALUE v_sockaddr_in_struct;
+VALUE v_sctp_status_struct;
+VALUE v_sctp_rtoinfo_struct;
+VALUE v_sctp_associnfo_struct;
+VALUE v_sctp_default_send_params_struct;
+#if !defined(IOV_MAX)
+#if defined(_SC_IOV_MAX)
+#define IOV_MAX (sysconf(_SC_IOV_MAX))
+#else
+// Assume infinity, or let the syscall return with error
+#define IOV_MAX INT_MAX
+#endif
+#endif
+
+#define ARY2IOVEC(iov,ary) \
+ do { \
+ VALUE *cur; \
+ struct iovec *tmp; \
+ long n; \
+ cur = RARRAY_PTR(ary); \
+ n = RARRAY_LEN(ary); \
+ iov = tmp = alloca(sizeof(struct iovec) * n); \
+ for (; --n >= 0; tmp++, cur++) { \
+ if (TYPE(*cur) != T_STRING) \
+ rb_raise(rb_eArgError, "must be an array of strings"); \
+ tmp->iov_base = RSTRING_PTR(*cur); \
+ tmp->iov_len = RSTRING_LEN(*cur); \
+ } \
+ } while (0)
+
+// TODO: Yes, I know I need to update the signature.
VALUE convert_sockaddr_in_to_struct(struct sockaddr_in* addr){
- char ipbuf[16];
+ char ipbuf[INET6_ADDRSTRLEN];
- inet_ntop(addr->sin_family, &(((struct sockaddr_in *)addr)->sin_addr), ipbuf, sizeof(ipbuf));
+ if(addr->sin_family == AF_INET6)
+ inet_ntop(addr->sin_family, &(((struct sockaddr_in6 *)addr)->sin6_addr), ipbuf, sizeof(ipbuf));
+ else
+ inet_ntop(addr->sin_family, &(((struct sockaddr_in *)addr)->sin_addr), ipbuf, sizeof(ipbuf));
return rb_struct_new(v_sockaddr_in_struct,
INT2NUM(addr->sin_family),
INT2NUM(ntohs(addr->sin_port)),
rb_str_new2(ipbuf)
@@ -314,10 +347,55 @@
sctp_freeladdrs(addrs);
return v_array;
}
+#ifdef HAVE_SCTP_SENDV
+static VALUE rsctp_sendv(VALUE self, VALUE v_messages){
+ struct iovec* iov;
+ struct sockaddr* addrs[8];
+ struct sctp_sndinfo info;
+ int sock_fd, num_bytes, size;
+
+ Check_Type(v_messages, T_ARRAY);
+ bzero(&addrs, sizeof(addrs));
+
+ sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
+
+ Check_Type(v_messages, T_ARRAY);
+ size = RARRAY_LEN(v_messages);
+
+ if(!size)
+ rb_raise(rb_eArgError, "Must contain at least one message");
+
+ if(size > IOV_MAX)
+ rb_raise(rb_eArgError, "Array size is greater than IOV_MAX");
+
+ ARY2IOVEC(iov, v_messages);
+
+ info.snd_flags = SCTP_UNORDERED;
+ info.snd_assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
+
+ num_bytes = sctp_sendv(
+ sock_fd,
+ iov,
+ size,
+ NULL,
+ 0,
+ &info,
+ sizeof(info),
+ SCTP_SENDV_SNDINFO,
+ 0
+ );
+
+ if(num_bytes < 0)
+ rb_raise(rb_eSystemCallError, "sctp_sendv: %s", strerror(errno));
+
+ return INT2NUM(num_bytes);
+}
+#endif
+
/*
* Send a message on an already-connected socket to a specific association.
*
* Example:
*
@@ -676,10 +754,11 @@
UINT2NUM(snp->sn_remote_error.sre_error),
UINT2NUM(snp->sn_remote_error.sre_assoc_id),
rb_ary_new4(snp->sn_remote_error.sre_length, v_temp)
);
break;
+#ifdef SCTP_SEND_FAILED_EVENT
case SCTP_SEND_FAILED_EVENT:
v_temp = ALLOCA_N(VALUE, snp->sn_send_failed_event.ssf_length);
for(i = 0; i < snp->sn_send_failed_event.ssf_length; i++){
v_temp[i] = UINT2NUM(snp->sn_send_failed_event.ssf_data[i]);
@@ -698,10 +777,28 @@
),
UINT2NUM(snp->sn_send_failed_event.ssf_assoc_id),
rb_ary_new4(snp->sn_send_failed_event.ssf_length, v_temp)
);
break;
+#else
+ case SCTP_SEND_FAILED:
+ v_temp = ALLOCA_N(VALUE, snp->sn_send_failed.ssf_length);
+
+ for(i = 0; i < snp->sn_send_failed.ssf_length; i++){
+ v_temp[i] = UINT2NUM(snp->sn_send_failed.ssf_data[i]);
+ }
+
+ v_notification = rb_struct_new(v_send_failed_event_struct,
+ UINT2NUM(snp->sn_send_failed.ssf_type),
+ UINT2NUM(snp->sn_send_failed.ssf_length),
+ UINT2NUM(snp->sn_send_failed.ssf_error),
+ Qnil,
+ UINT2NUM(snp->sn_send_failed.ssf_assoc_id),
+ rb_ary_new4(snp->sn_send_failed.ssf_length, v_temp)
+ );
+ break;
+#endif
case SCTP_SHUTDOWN_EVENT:
v_notification = rb_struct_new(v_shutdown_event_struct,
UINT2NUM(snp->sn_shutdown_event.sse_type),
UINT2NUM(snp->sn_shutdown_event.sse_length),
UINT2NUM(snp->sn_shutdown_event.sse_assoc_id)
@@ -858,13 +955,16 @@
events.sctp_association_event = 1;
if(RTEST(rb_hash_aref2(v_options, "address")))
events.sctp_address_event = 1;
- // Use the new version
if(RTEST(rb_hash_aref2(v_options, "send_failure")))
+#ifdef HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_SEND_FAILURE_EVENT
+ events.sctp_send_failure_event = 1;
+#else
events.sctp_send_failure_event_event = 1;
+#endif
if(RTEST(rb_hash_aref2(v_options, "peer_error")))
events.sctp_peer_error_event = 1;
if(RTEST(rb_hash_aref2(v_options, "shutdown")))
@@ -940,10 +1040,65 @@
rb_iv_set(self, "@sock_fd", INT2NUM(new_sock_fd));
return self;
}
+static VALUE rsctp_get_default_send_params(VALUE self){
+ int sock_fd;
+ socklen_t size;
+ sctp_assoc_t assoc_id;
+ struct sctp_sndrcvinfo sndrcv;
+
+ bzero(&sndrcv, sizeof(sndrcv));
+
+ sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
+ size = sizeof(struct sctp_sndrcvinfo);
+
+ if(sctp_opt_info(sock_fd, assoc_id, SCTP_DEFAULT_SEND_PARAM, (void*)&sndrcv, &size) < 0)
+ rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
+
+ return rb_struct_new(
+ v_sctp_default_send_params_struct,
+ INT2NUM(sndrcv.sinfo_stream),
+ INT2NUM(sndrcv.sinfo_ssn),
+ INT2NUM(sndrcv.sinfo_flags),
+ INT2NUM(sndrcv.sinfo_ppid),
+ INT2NUM(sndrcv.sinfo_context),
+ INT2NUM(sndrcv.sinfo_timetolive),
+ INT2NUM(sndrcv.sinfo_tsn),
+ INT2NUM(sndrcv.sinfo_cumtsn),
+ INT2NUM(sndrcv.sinfo_assoc_id)
+ );
+}
+
+static VALUE rsctp_get_association_info(VALUE self){
+ int sock_fd;
+ socklen_t size;
+ sctp_assoc_t assoc_id;
+ struct sctp_assocparams assoc;
+
+ bzero(&assoc, sizeof(assoc));
+
+ sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
+ size = sizeof(struct sctp_assocparams);
+
+ if(sctp_opt_info(sock_fd, assoc_id, SCTP_ASSOCINFO, (void*)&assoc, &size) < 0)
+ rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
+
+ return rb_struct_new(
+ v_sctp_associnfo_struct,
+ INT2NUM(assoc.sasoc_assoc_id),
+ INT2NUM(assoc.sasoc_asocmaxrxt),
+ INT2NUM(assoc.sasoc_number_peer_destinations),
+ INT2NUM(assoc.sasoc_peer_rwnd),
+ INT2NUM(assoc.sasoc_local_rwnd),
+ INT2NUM(assoc.sasoc_cookie_life)
+ );
+}
+
static VALUE rsctp_shutdown(int argc, VALUE* argv, VALUE self){
int how, sock_fd;
VALUE v_how;
sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
@@ -962,10 +1117,77 @@
rb_raise(rb_eSystemCallError, "shutdown: %s", strerror(errno));
return self;
}
+static VALUE rsctp_get_retransmission_info(VALUE self){
+ int sock_fd;
+ socklen_t size;
+ sctp_assoc_t assoc_id;
+ struct sctp_rtoinfo rto;
+
+ bzero(&rto, sizeof(rto));
+
+ sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
+ size = sizeof(struct sctp_rtoinfo);
+
+ if(sctp_opt_info(sock_fd, assoc_id, SCTP_RTOINFO, (void*)&rto, &size) < 0)
+ rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
+
+ return rb_struct_new(
+ v_sctp_rtoinfo_struct,
+ INT2NUM(rto.srto_assoc_id),
+ INT2NUM(rto.srto_initial),
+ INT2NUM(rto.srto_max),
+ INT2NUM(rto.srto_min)
+ );
+}
+
+static VALUE rsctp_get_status(VALUE self){
+ int sock_fd;
+ socklen_t size;
+ sctp_assoc_t assoc_id;
+ struct sctp_status status;
+ struct sctp_paddrinfo* spinfo;
+ char tmpname[INET_ADDRSTRLEN];
+
+ bzero(&status, sizeof(status));
+
+ sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
+ size = sizeof(struct sctp_status);
+
+ if(sctp_opt_info(sock_fd, assoc_id, SCTP_STATUS, (void*)&status, &size) < 0)
+ rb_raise(rb_eSystemCallError, "sctp_opt_info: %s", strerror(errno));
+
+ spinfo = &status.sstat_primary;
+
+ if (spinfo->spinfo_address.ss_family == AF_INET6) {
+ struct sockaddr_in6 *sin6;
+ sin6 = (struct sockaddr_in6 *)&spinfo->spinfo_address;
+ inet_ntop(AF_INET6, &sin6->sin6_addr, tmpname, sizeof (tmpname));
+ }
+ else {
+ struct sockaddr_in *sin;
+ sin = (struct sockaddr_in *)&spinfo->spinfo_address;
+ inet_ntop(AF_INET, &sin->sin_addr, tmpname, sizeof (tmpname));
+ }
+
+ return rb_struct_new(v_sctp_status_struct,
+ INT2NUM(status.sstat_assoc_id),
+ INT2NUM(status.sstat_state),
+ INT2NUM(status.sstat_rwnd),
+ INT2NUM(status.sstat_unackdata),
+ INT2NUM(status.sstat_penddata),
+ INT2NUM(status.sstat_instrms),
+ INT2NUM(status.sstat_outstrms),
+ INT2NUM(status.sstat_fragmentation_point),
+ rb_str_new2(tmpname)
+ );
+}
+
void Init_socket(){
mSCTP = rb_define_module("SCTP");
cSocket = rb_define_class_under(mSCTP, "Socket", rb_cObject);
v_sndrcv_struct = rb_struct_define(
@@ -1014,21 +1236,50 @@
v_sockaddr_in_struct = rb_struct_define(
"SockAddrIn", "family", "port", "address", NULL
);
+ v_sctp_status_struct = rb_struct_define(
+ "Status", "association_id", "state", "receive_window", "unacknowledged_data",
+ "pending_data", "inbound_streams", "outbound_streams", "fragmentation_point", "primary", NULL
+ );
+
+ v_sctp_rtoinfo_struct = rb_struct_define(
+ "RetransmissionInfo", "association_id", "initial", "max", "min", NULL
+ );
+
+ v_sctp_associnfo_struct = rb_struct_define(
+ "AssociationInfo", "association_id", "max_retransmission_count",
+ "number_peer_destinations", "peer_receive_window", "local_receive_window",
+ "cookie_life", NULL
+ );
+
+ v_sctp_default_send_params_struct = rb_struct_define(
+ "DefaultSendParams", "stream", "ssn", "flags", "ppid", "context",
+ "ttl", "tsn", "cumtsn", "association_id", NULL
+ );
+
rb_define_method(cSocket, "initialize", rsctp_init, -1);
rb_define_method(cSocket, "bind", rsctp_bind, -1);
rb_define_method(cSocket, "close", rsctp_close, 0);
rb_define_method(cSocket, "connect", rsctp_connect, -1);
rb_define_method(cSocket, "getpeernames", rsctp_getpeernames, 0);
rb_define_method(cSocket, "getlocalnames", rsctp_getlocalnames, 0);
+ rb_define_method(cSocket, "get_status", rsctp_get_status, 0);
+ rb_define_method(cSocket, "get_default_send_params", rsctp_get_default_send_params, 0);
+ rb_define_method(cSocket, "get_retransmission_info", rsctp_get_retransmission_info, 0);
+ rb_define_method(cSocket, "get_association_info", rsctp_get_association_info, 0);
rb_define_method(cSocket, "listen", rsctp_listen, -1);
rb_define_method(cSocket, "peeloff!", rsctp_peeloff, 1);
rb_define_method(cSocket, "recvmsg", rsctp_recvmsg, -1);
rb_define_method(cSocket, "send", rsctp_send, 1);
+
+#ifdef HAVE_SCTP_SENDV
+ rb_define_method(cSocket, "sendv", rsctp_sendv, 1);
+#endif
+
rb_define_method(cSocket, "sendmsg", rsctp_sendmsg, 1);
rb_define_method(cSocket, "set_initmsg", rsctp_set_initmsg, 1);
rb_define_method(cSocket, "shutdown", rsctp_shutdown, -1);
rb_define_method(cSocket, "subscribe", rsctp_subscribe, 1);
@@ -1036,12 +1287,12 @@
rb_define_attr(cSocket, "type", 1, 1);
rb_define_attr(cSocket, "sock_fd", 1, 1);
rb_define_attr(cSocket, "association_id", 1, 1);
rb_define_attr(cSocket, "port", 1, 1);
- /* 0.0.4: The version of this library */
- rb_define_const(cSocket, "VERSION", rb_str_new2("0.0.4"));
+ /* 0.0.5: The version of this library */
+ rb_define_const(cSocket, "VERSION", rb_str_new2("0.0.5"));
/* send flags */
/* Message is unordered */
rb_define_const(cSocket, "SCTP_UNORDERED", INT2NUM(SCTP_UNORDERED));
@@ -1057,6 +1308,18 @@
/* Send to all associations */
rb_define_const(cSocket, "SCTP_SENDALL", INT2NUM(SCTP_SENDALL));
rb_define_const(cSocket, "MSG_NOTIFICATION", INT2NUM(MSG_NOTIFICATION));
+
+ // ASSOCIATION STATES //
+
+ rb_define_const(cSocket, "SCTP_EMPTY", INT2NUM(SCTP_EMPTY));
+ rb_define_const(cSocket, "SCTP_CLOSED", INT2NUM(SCTP_CLOSED));
+ rb_define_const(cSocket, "SCTP_COOKIE_WAIT", INT2NUM(SCTP_COOKIE_WAIT));
+ rb_define_const(cSocket, "SCTP_COOKIE_ECHOED", INT2NUM(SCTP_COOKIE_ECHOED));
+ rb_define_const(cSocket, "SCTP_ESTABLISHED", INT2NUM(SCTP_ESTABLISHED));
+ rb_define_const(cSocket, "SCTP_SHUTDOWN_PENDING", INT2NUM(SCTP_SHUTDOWN_PENDING));
+ rb_define_const(cSocket, "SCTP_SHUTDOWN_SENT", INT2NUM(SCTP_SHUTDOWN_SENT));
+ rb_define_const(cSocket, "SCTP_SHUTDOWN_RECEIVED", INT2NUM(SCTP_SHUTDOWN_RECEIVED));
+ rb_define_const(cSocket, "SCTP_SHUTDOWN_ACK_SENT", INT2NUM(SCTP_SHUTDOWN_ACK_SENT));
}