derhuerst / node-sockopt

getsockopt & setsockopt for Node.js sockets.
https://github.com/derhuerst/node-sockopt#sockopt
ISC License
9 stars 2 forks source link

Protocol not available (92) - Should this work in ubuntu? #4

Open markwylde opened 4 years ago

markwylde commented 4 years ago

I'm trying to use this in linux ubuntu to get the SO_ORIGINAL_DST from a tcp socket that is redirect via iptables.

Looking at some working golang code:

const SO_ORIGINAL_DST = 80

...

   addr, err :=  syscall.GetsockoptIPv6Mreq(int(clientConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
    log.Debugf("getOriginalDst(): SO_ORIGINAL_DST=%+v\n", addr)
    if err != nil {
        log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err)
        return
    }

It would appear that IPPROTO_IP = 0 and SO_ORIGINAL_DST = 80.

I would have expected (although I'm sure I'm completely missing something) the following code to work:

const net = require('net');
const {getsockopt, setsockopt} = require('.');

const SOL_SOCKET = 1;

const server = net.createServer((c) => {
    console.log(getsockopt(c, SOL_SOCKET, 80))
});
server.listen(8080, () => {
  console.log('server bound');
});

But it returns:

/root/tproxy/node-sockopt/index.js:17
    return _getsockopt(fd(socket), level, flagName)
           ^

TypeError: getsockopt failed: Protocol not available (92)
    at getsockopt (/root/tproxy/node-sockopt/index.js:17:9)
    at Socket.<anonymous> (/root/tproxy/node-sockopt/test.js:19:22)
    at Socket.onListening (dgram.js:225:10)
    at Socket.emit (events.js:315:20)
    at startListening (dgram.js:150:10)
    at dgram.js:345:7
    at processTicksAndRejections (internal/process/task_queues.js:85:21)

It looks like my combination of 1 and 80 is either incorrect, or I'm using this completely wrong.

Next I ran a couple of loops to try every possibility I could think of. But I can't see 80 working for any level + flagName combination.

    for (let x = 0; x < 100; x++ ) {
        for (let y = 0; y < 100; y++ ) {
            try {
                console.log({x,y}, getsockopt(c, x, y))
            } catch (error) {
            }
        }
    }
{ x: 0, y: 1 } 0
{ x: 0, y: 2 } 64
{ x: 0, y: 3 } 0
{ x: 0, y: 4 } 74898496
{ x: 0, y: 6 } 0
{ x: 0, y: 7 } 0
{ x: 0, y: 8 } 0
{ x: 0, y: 9 } 0
{ x: 0, y: 10 } 1
{ x: 0, y: 11 } 0
{ x: 0, y: 12 } 0
{ x: 0, y: 13 } 0
{ x: 0, y: 14 } 1500
{ x: 0, y: 15 } 0
{ x: 0, y: 18 } 0
{ x: 0, y: 19 } 0
{ x: 0, y: 20 } 0
{ x: 0, y: 21 } 0
{ x: 0, y: 22 } 0
{ x: 0, y: 23 } 0
{ x: 0, y: 24 } 0
{ x: 0, y: 25 } 0
{ x: 0, y: 32 } 0
{ x: 0, y: 33 } 64
{ x: 0, y: 34 } 1
{ x: 0, y: 49 } 0
{ x: 0, y: 50 } 0
{ x: 1, y: 1 } 0
{ x: 1, y: 2 } 1
{ x: 1, y: 3 } 1
{ x: 1, y: 4 } 0
{ x: 1, y: 5 } 0
{ x: 1, y: 6 } 0
{ x: 1, y: 7 } 130560
{ x: 1, y: 8 } 131072
{ x: 1, y: 9 } 0
{ x: 1, y: 10 } 0
{ x: 1, y: 11 } 0
{ x: 1, y: 12 } 0
{ x: 1, y: 13 } 0
{ x: 1, y: 14 } 0
{ x: 1, y: 15 } 0
{ x: 1, y: 16 } 0
{ x: 1, y: 17 } 0
{ x: 1, y: 18 } 1
{ x: 1, y: 19 } 1
{ x: 1, y: 20 } 0
{ x: 1, y: 21 } 0
{ x: 1, y: 25 } 0
{ x: 1, y: 26 } 75578616
{ x: 1, y: 28 } 1020264458
{ x: 1, y: 29 } 0
{ x: 1, y: 30 } 0
{ x: 1, y: 34 } 0
{ x: 1, y: 35 } 0
{ x: 1, y: 36 } 0
{ x: 1, y: 37 } 0
{ x: 1, y: 38 } 6
{ x: 1, y: 39 } 10
{ x: 1, y: 40 } 0
{ x: 1, y: 41 } 0
{ x: 1, y: 43 } 0
{ x: 1, y: 44 } 0
{ x: 1, y: 45 } 0
{ x: 1, y: 46 } 0
{ x: 1, y: 47 } -1
{ x: 1, y: 48 } 64
{ x: 1, y: 49 } 0
{ x: 1, y: 55 } 2304
{ x: 1, y: 56 } 0
{ x: 1, y: 60 } 0
{ x: 6, y: 1 } 0
{ x: 6, y: 2 } 1448
{ x: 6, y: 3 } 0
{ x: 6, y: 4 } 7200
{ x: 6, y: 5 } 75
{ x: 6, y: 6 } 9
{ x: 6, y: 7 } 6
{ x: 6, y: 8 } 60
{ x: 6, y: 9 } 0
{ x: 6, y: 10 } 64076
{ x: 6, y: 11 } 1
{ x: 6, y: 12 } 1
{ x: 6, y: 13 } 7496290
{ x: 6, y: 16 } 0
{ x: 6, y: 17 } 0
{ x: 6, y: 18 } 0
{ x: 6, y: 19 } 0
{ x: 6, y: 23 } 0
{ x: 6, y: 24 } -1560222177
{ x: 6, y: 25 } 0
{ x: 6, y: 26 } 0
{ x: 6, y: 27 } 0
{ x: 6, y: 28 } -1645237088
{ x: 6, y: 30 } 0
{ x: 6, y: 31 } -1645237088
{ x: 6, y: 33 } 0
{ x: 6, y: 34 } 0
{ x: 41, y: 1 } 10
{ x: 41, y: 2 } 0
{ x: 41, y: 3 } 0
{ x: 41, y: 4 } 0
{ x: 41, y: 5 } 0
{ x: 41, y: 6 } -1645237088
{ x: 41, y: 8 } 0
{ x: 41, y: 11 } 0
{ x: 41, y: 16 } 64
{ x: 41, y: 17 } 4
{ x: 41, y: 18 } 64
{ x: 41, y: 19 } 1
{ x: 41, y: 23 } 1
{ x: 41, y: 24 } 1500
{ x: 41, y: 25 } 0
{ x: 41, y: 26 } 0
{ x: 41, y: 33 } 0
{ x: 41, y: 49 } 0
{ x: 41, y: 51 } 0
{ x: 41, y: 53 } 0
{ x: 41, y: 54 } -1645237088
{ x: 41, y: 55 } -1645237088
{ x: 41, y: 56 } 0
{ x: 41, y: 57 } -1645237088
{ x: 41, y: 58 } 0
{ x: 41, y: 59 } -1645237088
{ x: 41, y: 60 } 0
{ x: 41, y: 62 } 0
{ x: 41, y: 66 } 0
{ x: 41, y: 67 } 0
{ x: 41, y: 70 } 1
{ x: 41, y: 72 } 1280
{ x: 41, y: 73 } 0
{ x: 41, y: 74 } 0
{ x: 41, y: 75 } 0
{ x: 41, y: 76 } 0
{ x: 41, y: 77 } 0
{ x: 41, y: 78 } 0

Could you please point me in the right direction?

derhuerst commented 4 years ago

Although I wrote this package, I'm not familiar with all aspects of socket/network programming, and I have forgotten most of it again. 🙈

Given that the Go syscall.GetsockoptIPv6Mreq API takes 3 arguments fd, level & opt, I assume that you need to pass IPPROTO_IP to getsockopt as well (instead of SOL_SOCKET). Can you try that?

markwylde commented 4 years ago

No worries @derhuerst . I'm in the same boat and your library has already helped me so much. I'll keep playing around and post if I work out a solution.

derhuerst commented 4 years ago

Have you tried passing IPPROTO_IP as the 2nd argument?

markwylde commented 4 years ago

Yeah I've even tried removing the types from node and hardcoding everything, just incase it was a type issue:

Napi::Value Getsockopt(const Napi::CallbackInfo& args) {
    Napi::Env env = args.Env();

    if (!args[0].IsNumber()) {
        Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
        return env.Null();
    }

    int socket = args[0].As<Napi::Number>().Int32Value();

    struct sockaddr_in *destaddr;
    socklen_t socklen = sizeof(*destaddr);

    if (0 != getsockopt(socket, IPPROTO_IP, SO_ORIGINAL_DST, destaddr, &socklen)) {
        char * msg = (char *) malloc(sizeof(char) * 100);
        sprintf(msg, "getsockopt failed: %s (%d)", strerror(errno), errno);
        // todo: expose `errno`
        Napi::TypeError::New(env, msg).ThrowAsJavaScriptException();
        return env.Null();
    }

    return Napi::Number::New(env, 1);
}

At least I'm getting a different error :S

getsockopt failed: (-1) Bad address (14)
derhuerst commented 4 years ago

At this point I'm out of ideas, sorry. Can you check if it works in plain C/C++, in order to check if it's due to node-sockopt or due to your general setup?

markwylde commented 4 years ago

Me too @derhuerst . Thanks for helping. Linux is hard :(

derhuerst commented 4 years ago

I'm going to close this issue for now. Please reopen if you have more questions related to this topic, or if there's a bug in node-sockopt.

derhuerst commented 2 years ago

I've just come across this bug in a CI run: https://github.com/derhuerst/node-sockopt/runs/6378445051?check_suite_focus=true#step:6:12

Not sure what to do though.