ext/sctp/socket.c in sctp-socket-0.0.3 vs ext/sctp/socket.c in sctp-socket-0.0.4
- old
+ new
@@ -14,11 +14,24 @@
VALUE v_shutdown_event_struct;
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 convert_sockaddr_in_to_struct(struct sockaddr_in* addr){
+ char ipbuf[16];
+
+ 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)
+ );
+}
+
// Helper function to get a hash value via string or symbol.
VALUE rb_hash_aref2(VALUE v_hash, const char* key){
VALUE v_key, v_val;
v_key = rb_str_new2(key);
@@ -33,12 +46,19 @@
/*
* Create and return a new SCTP::Socket instance. You may optionally pass in
* a domain (aka family) value and socket type. By default these are AF_INET
* and SOCK_SEQPACKET, respectively.
*
+ * There are only two supported families: SOCK_SEQPACKET for the creation
+ * of a one-to-many socket, and SOCK_STREAM for the creation of a
+ * one-to-one socket.
+ *
* Example:
*
+ * require 'socket'
+ * require 'sctp/socket'
+ *
* socket1 = SCTP::Socket.new
* socket2 = SCTP::Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
*/
static VALUE rsctp_init(int argc, VALUE* argv, VALUE self){
int sock_fd;
@@ -200,11 +220,11 @@
sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
if(sctp_connectx(sock_fd, (struct sockaddr *) addrs, num_ip, &assoc) < 0)
rb_raise(rb_eSystemCallError, "sctp_connectx: %s", strerror(errno));
- rb_iv_set(self, "@assocation_id", INT2NUM(assoc));
+ rb_iv_set(self, "@association_id", INT2NUM(assoc));
return self;
}
/*
@@ -295,10 +315,103 @@
return v_array;
}
/*
+ * Send a message on an already-connected socket to a specific association.
+ *
+ * Example:
+ *
+ * socket = SCTP::Socket.new
+ * socket.connect(:port => 42000, :addresses => ['10.0.4.5', '10.0.5.5'])
+ *
+ * socket.send(:message => "Hello World")
+ * socket.send(:message => "Hello World", :association_id => 37)
+ *
+ */
+static VALUE rsctp_send(VALUE self, VALUE v_options){
+ uint16_t stream;
+ uint32_t ppid, send_flags, ctrl_flags, ttl, context;
+ ssize_t num_bytes;
+ int sock_fd;
+ sctp_assoc_t assoc_id;
+ struct sctp_sndrcvinfo info;
+ VALUE v_msg, v_stream, v_ppid, v_context, v_send_flags, v_ctrl_flags, v_ttl, v_assoc_id;
+
+ Check_Type(v_options, T_HASH);
+
+ v_msg = rb_hash_aref2(v_options, "message");
+ v_stream = rb_hash_aref2(v_options, "stream");
+ v_ppid = rb_hash_aref2(v_options, "ppid");
+ v_context = rb_hash_aref2(v_options, "context");
+ v_send_flags = rb_hash_aref2(v_options, "send_flags");
+ v_ctrl_flags = rb_hash_aref2(v_options, "control_flags");
+ v_ttl = rb_hash_aref2(v_options, "ttl");
+ v_assoc_id = rb_hash_aref2(v_options, "association_id");
+
+ if(NIL_P(v_stream))
+ stream = 0;
+ else
+ stream = NUM2INT(v_stream);
+
+ if(NIL_P(v_send_flags))
+ send_flags = 0;
+ else
+ send_flags = NUM2INT(v_send_flags);
+
+ if(NIL_P(v_ctrl_flags))
+ ctrl_flags = 0;
+ else
+ ctrl_flags = NUM2INT(v_ctrl_flags);
+
+ if(NIL_P(v_ttl)){
+ ttl = 0;
+ }
+ else{
+ ttl = NUM2INT(v_ttl);
+ send_flags |= SCTP_PR_SCTP_TTL;
+ }
+
+ if(NIL_P(v_ppid))
+ ppid = 0;
+ else
+ ppid = NUM2INT(v_ppid);
+
+ if(NIL_P(v_context))
+ context = 0;
+ else
+ context = NUM2INT(v_context);
+
+ if(NIL_P(v_assoc_id))
+ assoc_id = NUM2INT(rb_iv_get(self, "@association_id"));
+ else
+ assoc_id = NUM2INT(v_assoc_id);
+
+ info.sinfo_stream = stream;
+ info.sinfo_flags = send_flags;
+ info.sinfo_ppid = ppid;
+ info.sinfo_context = context;
+ info.sinfo_timetolive = ttl;
+ info.sinfo_assoc_id = assoc_id;
+
+ sock_fd = NUM2INT(rb_iv_get(self, "@sock_fd"));
+
+ num_bytes = sctp_send(
+ sock_fd,
+ StringValueCStr(v_msg),
+ RSTRING_LEN(v_msg),
+ &info,
+ ctrl_flags
+ );
+
+ if(num_bytes < 0)
+ rb_raise(rb_eSystemCallError, "sctp_send: %s", strerror(errno));
+
+ return INT2NUM(num_bytes);
+}
+
+/*
* Transmit a message to an SCTP endpoint. The following hash of options
* is permitted:
*
* :message -> The message to send to the endpoint. Mandatory.
* :stream -> The SCTP stream number you wish to send the message on.
@@ -349,16 +462,19 @@
stream = NUM2INT(v_stream);
if(NIL_P(v_flags))
flags = 0;
else
- flags = NUM2INT(v_stream);
+ flags = NUM2INT(v_flags);
- if(NIL_P(v_ttl))
+ if(NIL_P(v_ttl)){
ttl = 0;
- else
+ }
+ else{
ttl = NUM2INT(v_ttl);
+ flags |= SCTP_PR_SCTP_TTL;
+ }
if(NIL_P(v_ppid))
ppid = 0;
else
ppid = NUM2INT(v_ppid);
@@ -632,11 +748,12 @@
UINT2NUM(sndrcvinfo.sinfo_flags),
UINT2NUM(sndrcvinfo.sinfo_ppid),
UINT2NUM(sndrcvinfo.sinfo_context),
UINT2NUM(sndrcvinfo.sinfo_timetolive),
UINT2NUM(sndrcvinfo.sinfo_assoc_id),
- v_notification
+ v_notification,
+ convert_sockaddr_in_to_struct(&clientaddr)
);
}
/*
* Set the initial parameters used by the socket when sending out the INIT message.
@@ -691,11 +808,11 @@
* Subscribe to various notification types, which will generate additional
* data that the socket may receive. The possible notification types are
* as follows:
*
* :association
- * - A change has occurred to an assocation, either a new one has begun or an existing one has end.
+ * - A change has occurred to an association, either a new one has begun or an existing one has end.
*
* :address
* - The state of one of the peer's addresses has experienced a change.
*
* :send_failure
@@ -851,11 +968,11 @@
mSCTP = rb_define_module("SCTP");
cSocket = rb_define_class_under(mSCTP, "Socket", rb_cObject);
v_sndrcv_struct = rb_struct_define(
"SendReceiveInfo", "message", "stream", "flags",
- "ppid", "context", "ttl", "association_id", "notification", NULL
+ "ppid", "context", "ttl", "association_id", "notification", "client", NULL
);
v_assoc_change_struct = rb_struct_define(
"AssocChange", "type", "length", "state", "error",
"outbound_streams", "inbound_streams", "association_id", "info", NULL
@@ -893,20 +1010,25 @@
v_auth_event_struct = rb_struct_define(
"AuthEvent", "type", "length", "key_number", "indication", "association_id", NULL
);
+ v_sockaddr_in_struct = rb_struct_define(
+ "SockAddrIn", "family", "port", "address", 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, "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);
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);
@@ -914,8 +1036,27 @@
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.2: The version of this library */
- rb_define_const(cSocket, "VERSION", rb_str_new2("0.0.3"));
+ /* 0.0.4: The version of this library */
+ rb_define_const(cSocket, "VERSION", rb_str_new2("0.0.4"));
+
+ /* send flags */
+
+ /* Message is unordered */
+ rb_define_const(cSocket, "SCTP_UNORDERED", INT2NUM(SCTP_UNORDERED));
+
+ /* Override the primary address */
+ rb_define_const(cSocket, "SCTP_ADDR_OVER", INT2NUM(SCTP_ADDR_OVER));
+
+ /* Send an ABORT to peer */
+ rb_define_const(cSocket, "SCTP_ABORT", INT2NUM(SCTP_ABORT));
+
+ /* Start a shutdown procedure */
+ rb_define_const(cSocket, "SCTP_EOF", INT2NUM(SCTP_EOF));
+
+ /* Send to all associations */
+ rb_define_const(cSocket, "SCTP_SENDALL", INT2NUM(SCTP_SENDALL));
+
+ rb_define_const(cSocket, "MSG_NOTIFICATION", INT2NUM(MSG_NOTIFICATION));
}