1

I have a C function that I want to call using Java via SWIG but I'm unsure how to handle the sockaddr_in C structure. Anyone have any examples on how I can handle the sockaddr_in?

c12
  • 9,557
  • 48
  • 157
  • 253
  • Do you want to make it compatible with the existing `java.net.InetAddress` class, or just wrap `sockaddr_in` with a usable interface? – Flexo Nov 15 '11 at 19:39
  • I want to wrap it with a usable interface... – c12 Nov 15 '11 at 20:56

2 Answers2

2

There's actually an article on wrapping sockaddr_in on swig.org, although it looks slightly old now.

Basically what they did was write a function that creates a new sockaddr_in for you, taking arguments for the values that need to be filled in as things that are easy to pass around in Java. This is a slightly updated, trimmed version of the linked article:

%module sock          // Name of our module
%{
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

/* Set some values in the sockaddr_in structure */
struct sockaddr *new_sockaddr_in(short family, unsigned long hostid, int port) {
        struct sockaddr_in *addr;
        addr = (struct sockaddr_in *) malloc(sizeof(struct sockaddr_in));
        bzero((char *) addr, sizeof(struct sockaddr_in));
        addr->sin_family = family;
        addr->sin_addr.s_addr = hostid;
        addr->sin_port = htons(port);
        return (struct sockaddr *) addr;
}
%}

// Add these constants
enum {AF_UNIX, AF_INET, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW,
      IPPROTO_UDP, IPPROTO_TCP, INADDR_ANY};

#define  SIZEOF_SOCKADDR  sizeof(struct sockaddr)

// Wrap these functions
struct sockaddr *new_sockaddr_in(short family, unsigned long, int port);

There's a nicer way of wrapping this with SWIG though, we can write a typemap to use java.net.InetSocketAddress instead, which will feel far more "natural" on the Java side of the interface:

%typemap(jni) sockaddr_in *ADDR "jobject"
%typemap(jtype) sockaddr_in *ADDR "java.net.InetSocketAddress"
%typemap(jstype) sockaddr_in *ADDR "java.net.InetSocketAddress"

%typemap(in) (sockaddr_in *ADDR) {
  $1 = new sockaddr_in;
  $1->sin_family = AF_INET;
  jclass inetsockaddr = jenv->FindClass("java/net/InetSocketAddress");
  assert(inetsockaddr);
  // TODO: check return
  jmethodID pmid,addrmid,ipbytemid;
  pmid = jenv->GetMethodID(inetsockaddr, "getPort", "()I");
  assert(pmid);
  jint port = jenv->CallIntMethod($input, pmid);
  $1->sin_port = htons(port);
  jclass inetaddr = jenv->FindClass("java/net/InetAddress");
  assert(inetaddr);
  addrmid = jenv->GetMethodID(inetsockaddr, "getAddress", "()Ljava/net/InetAddress;");
  assert(addrmid);
  jobject addrobj = jenv->CallObjectMethod($input, addrmid);
  assert(addrobj);
  ipbytemid = jenv->GetMethodID(inetaddr, "getAddress", "()[B");
  assert(ipbytemid);
  jbyteArray barr = static_cast<jbyteArray>(jenv->CallObjectMethod(addrobj, ipbytemid));
  assert(barr);
  jbyte *bytes = jenv->GetByteArrayElements(barr, 0);
  assert(bytes);
  memcpy(&$1->sin_addr.s_addr, bytes, 4);
  $1->sin_addr.s_addr = htonl($1->sin_addr.s_addr);
  jenv->ReleaseByteArrayElements(barr, bytes, JNI_ABORT); // No changes copied back
}

%typemap(freearg) (sockaddr_in *ADDR) {
  delete $1;
}

%typemap(javain) sockaddr_in *ADDR "$javainput"

Basically this calls the getAddress() and getPort() methods of java.net.InetSocketAddress and uses the result to create a struct sockaddr_in for the call.

Notes:

  1. I'm not 100% sure I've got the byte order right here
  2. We ought to support AF_INET6 properly too - we'd need to inspect the given InetSocketAddress to see which sub-class it is in the typemap itself.
  3. There's no out typemap. This is basically the reverse procedure, the JNI code will create new Java objects for us.
  4. The asserts are pretty ugly.

For completeness there's also a third possible way of wrapping this, which involves no JNI, but writing a little bit of Java. What we do is have SWIG wrap the struct sockaddr as in the first example, but then have the wrapped functions that use sockaddr return a java.net.InetSocketAddress object still and supply some code for converting between the two. I'll give an example with an "out" typemap, i.e. for returning from functions.

Given:

sockaddr_in *make_stuff();

we can wrap it with:

%typemap(jstype) sockaddr_in *make_stuff "java.net.InetSocketAddress"
%typemap(javaout) sockaddr_in *make_stuff {
  long cPtr = $jnicall;
  sockaddr_in s = new sockaddr_in(cPtr, true);
  byte[] bytes = new byte[4];
  for (int i = 0; i < 4; ++i) {
    bytes[i] = (byte)s.getAddr(i);
  }
  java.net.InetAddress addr = null;
  try {
    addr = java.net.InetAddress.getByAddress(bytes);
  }
  catch (java.net.UnknownHostException e) {
    return null;
  }
  return new java.net.InetSocketAddress(addr, s.getPort());
}

%immutable;
struct sockaddr_in{
   %rename(family) sin_family;
   short sin_family;
   %extend {
     unsigned short getPort() const {
       return ntohs($self->sin_port);
     }
     char getAddr(int byte) const {
       const char *ptr = reinterpret_cast<const char*>(&$self->sin_addr.s_addr);
       return ptr[byte];
     }
   }
};
%mutable;

void do_stuff(sockaddr_in *ADDR);

We've specified how to wrap a sockaddr_in directly, but also instructed the return from the function itself to be the more appropriate Java type (%typemap(jstype)) and provided a small amount of Java to perform the conversion (%typemap(javaout)). We could do similar for an in typemap too. This doesn't handle AF_INET6 addresses properly - I can't find an equivalent of InetAddress.getByAddress() for IPv6 addresses, so there should probably be an assert/exception for that case.

Flexo
  • 87,323
  • 22
  • 191
  • 272
  • I'll try and do this a better way though. – Flexo Nov 16 '11 at 08:39
  • @c12 I have an idea for a much better way that I'll try and find time to flesh out – Flexo Nov 16 '11 at 18:22
  • thanks...I'm going to post another question with some more specifics about my situation that I didn't state originally that maybe you can address and get credit for – c12 Nov 16 '11 at 18:58
  • http://stackoverflow.com/questions/8157401/change-swig-interface-file-to-allow-return-of-object-type-from-function – c12 Nov 16 '11 at 19:20
  • @c12 - I've added a proper typemap for this that makes functions take a normal `InetSocketAddress` as input now instead of a wrapped type. – Flexo Nov 19 '11 at 01:31
1

I'm sure there's a better answer, and I look forward to seeing it. But this seems to work initially.

In your module.i:

%include "stdint.i"

%{
#include <arpa/inet.h>
%}

struct in_addr {
    uint32_t s_addr;
};

struct sockaddr_in {
    uint16_t sin_port;
    struct in_addr sin_addr;
};
rstoner
  • 11
  • 1