macli has asked for the wisdom of the Perl Monks concerning the following question:

Hi monks:

I would like to run a c program in Perl, I found out Inline::C can do this. when I run the program as:

./p0fq_inlc.pl /var/run/p0f.sock 192.168.1.1 0 192.168.1.2 25

I always get:

Adress 268633816 Adress 268633864 Adress 268633880 Adress 268633896 Adress 268633912 [-] ERROR: Bad IP/port values.
I read Inline::C perldoc and Inline::C-Cookbook, still not understanding enough to solve the problem. there is some perl internal api involved which is beyond my Perl knowledge, advices are appreciated!

Here is the code:
use Inline C; die "usage: p0fq_inlc.pl p0f_socket src_ip src_port dst_ip dst_port" unless $#ARGV == 4; p0fq($ARGV[0], $ARGV[1], $ARGV[2], $ARGV[3], $ARGV[4]); __END__ __C__ #include <sys/un.h> #include "../types.h" #include "../p0f-query.h" #define debug(x...) fprintf(stderr,x) #define fatal(x...) do { debug("[-] ERROR: " x); exit(2); } while (0) #define pfatal(x) do { debug("[-] ERROR: "); perror(x); exit(2); } w +hile (0) int p0fq(SV* name1, ...) { Inline_Stack_Vars; struct sockaddr_un x; struct p0f_query query; struct p0f_response response; _u32 src_add,dst_add,src_port,dst_port; _s32 sock; int i; for (i = 0; i < Inline_Stack_Items; i++) { printf("Adress %d\n", SvPV(Inline_Stack_Item(i), PL_na)); } src_add = inet_addr(SvIVX(Inline_Stack_Item(1))); src_port = atoi(SvIVX(Inline_Stack_Item(2))); dst_add = inet_addr(SvIVX(Inline_Stack_Item(3))); dst_port = atoi(SvIVX(Inline_Stack_Item(4))); if (!dst_port || src_add == INADDR_NONE || dst_add == INADDR_NONE) fatal("Bad IP/port values.\n"); sock = socket(PF_UNIX,SOCK_STREAM,0); if (sock < 0) pfatal("socket"); memset(&x,0,sizeof(x)); x.sun_family=AF_UNIX; strncpy(x.sun_path,SvPV(Inline_Stack_Item(0), PL_na),63); if (connect(sock,(struct sockaddr*)&x,sizeof(x))) pfatal(SvPV(Inlin +e_Stack_Item(0), PL_na)); query.magic = QUERY_MAGIC; query.id = 0x12345678; query.type = QTYPE_FINGERPRINT; query.src_ad = src_add; query.dst_ad = dst_add; query.src_port = src_port; query.dst_port = dst_port; if (write(sock,&query,sizeof(query)) != sizeof(query)) fatal("Socket write error (timeout?).\n"); if (read(sock,&response,sizeof(response)) != sizeof(response)) fatal("Response read error (timeout?).\n"); if (response.magic != QUERY_MAGIC) fatal("Bad response magic.\n"); if (response.type == RESP_BADQUERY) fatal("P0f did not honor our query.\n"); if (response.type == RESP_NOMATCH) { printf("This connection is not (no longer?) in the cache.\n"); exit(3); } if (!response.genre[0]) { printf("Genre and OS details not recognized.\n"); } else { printf("Genre : %s\n",response.genre); printf("Details : %s\n",response.detail); if (response.dist != -1) printf("Distance : %d hops\n",response.di +st); } if (response.link[0]) printf("Link : %s\n",response.link); if (response.tos[0]) printf("Service : %s\n",response.tos); if (response.uptime != -1) printf("Uptime : %d hrs\n",response.up +time); if (response.score != NO_SCORE) printf("M-Score : %d%% (flags %x).\n",response.score,response.mfl +ags); if (response.fw) printf("The host is behind a firewall.\n"); if (response.nat) printf("The host is behind NAT or such.\n"); shutdown(sock,2); close(sock); return 0; }

Replies are listed 'Best First'.
Re: How to use Inline::C properly
by samtregar (Abbot) on Mar 06, 2007 at 23:35 UTC
    It looks to me like you're trying to treat a string containing an IP address ("192.168.1.1") as an integer:

       src_add  = inet_addr(SvIVX(Inline_Stack_Item(1)));

    That takes the first stack item, the SVPV "192.168.1.1", and tries to treat it as an integer by calling SvIVX on it. It's not an integer, so this won't work. I'm not sure if it returns garbage or 0 or what, but it certainly can't be what you want!

    -sam

      No, it is not what I want.:)
      I just could not figure out which api I should use. after reading the man page, I still got no clue :(

        src_add  = inet_addr(SvIVX(Inline_Stack_Item(1)));
        should be
        src_add  = inet_addr(SvPVX(Inline_Stack_Item(1)));

        Same for dst_add.

        Why expose yourself to these Perl internals if you don't need to (besides instructional reasons)?

        int p0fq(SV* name1, ...)

        is a bit of a messy signature (since you have to manually pull things off the stack later. Why not try

        int p0fq(char* socket, char* src_ip, int src_port, char * dst_ip, int dst_port)

        Inline will handle the ugly glue for you :)

        UPDATE change char* dst_port to int dst_port. Thanks to Util for catching that.

Re: How to use Inline::C properly
by geekphilosopher (Friar) on Mar 06, 2007 at 22:58 UTC

    Hello, fellow Vancouverite (and UBC'er at that):)

    I've never used Inline::C, though I work with an author of several Inline modules. Though this isn't directly answering your question, I'm wondering why you're using Perl at all for this task. You seem to have written a complete C program and then wrapped it in Perl - what's your motivation for doing so? What's preventing you from just writing the program in C?

      Hello geekphilosopher :)

      I did not write the c myself, I am just being lazy and thought I could try to run the c in Perl and see how it works :).

      there is a similar perl script which do the same thing as the c, but it is not portable on my ppc linux (yellow dog linux), see:

      http://perlmonks.org/?node=p0f

      since the c program works both on x86 and ppc linux, so I guess I could wrap it in Perl and solve the portability problem.

Re: How to use Inline::C properly
by philcrow (Priest) on Mar 07, 2007 at 13:59 UTC
    The folks at the inline mailing list (inline@perl.org) are likely to have some answers.

    Phil

      Thanks for the guide, I will ask there someday when my Perl knowledge grows more.