X Tutup
The Wayback Machine - https://web.archive.org/web/20220405131511/https://github.com/nodejs/node-addon-api/issues/1116
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

About socket-sendto Error? #1116

Open
southorange1228 opened this issue Dec 29, 2021 · 5 comments
Open

About socket-sendto Error? #1116

southorange1228 opened this issue Dec 29, 2021 · 5 comments
Labels

Comments

@southorange1228
Copy link

@southorange1228 southorange1228 commented Dec 29, 2021

i try to create a icmp packet with node-addon-api.
but i always get invalid argument error when called sendto.

// create socket
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) == -1) {
        fprintf(stderr, "Create RAW socket error:%s \n\a", strerror(errno));
        exit(1);
}

// send msg
void Ping::SendPacket() {
    if((sendto(sock_fd, send_pack, pack_size, 0, (struct sockaddr *)&send_addr, sizeof(send_addr))) < 0){
        fprintf(stderr, "Sendto error:%s \n\a", strerror(errno));
        exit(1);
    }
}

But,i can send msg when i created a socket in a normal cpp project.

Is there some difference with node-addon-api and normal cpp project? And do you need any more information?

@NickNaso
Copy link
Member

@NickNaso NickNaso commented Dec 29, 2021

Hi @southorange1228,
is your code a client code? Could you post more of your example? The normal cpp project and your addon implementation?

@southorange1228
Copy link
Author

@southorange1228 southorange1228 commented Dec 30, 2021

yeah, this is a client code.
And there is normal cpp project

// iping.h
#include <iostream>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>

using namespace std;

#define PACK_SIZE 32
#define IP_HEADER_SIZE 20
#define ICMP_ECHO 8
#define ICMP_ECHO_REPLY 0

struct PingOptions {
    char * addr;
    int timeout;
    int retry;
};

struct IP_HEADER {
    unsigned char header_length :4; // header of length
    unsigned char version :4;  // version
    unsigned char tos;  // type of service
    unsigned short total_length; // total length of packet
    unsigned short identifier; // id
    unsigned short frag_and_flags; // fragment and flag
    unsigned char ttl; // ttl
    unsigned char protocol; // protocol eg: TCP UDP etc.
    unsigned short checksum; // checksum
    unsigned long source_ip; // source ip
    unsigned long dest_ip;  // destination ip
} ;


struct ICMP_HEADER {
    unsigned char icmp_type;
    unsigned char icmp_code;
    unsigned short icmp_checksum;
    unsigned short icmp_id;
    unsigned short icmp_seq;
    unsigned long timestamp_s;
    unsigned long timestamp_us;
};



class Ping {
public:
    Ping();
    ~Ping();
  void CreateSocket();
  void SendPacket();
  void RecvPacket();



private:

    PingOptions options;

  unsigned short CheckSum(unsigned short *header,int length);
  int GeneratePacket();
  int ResolvePacket(int pack_size);

    std::string input_domain;
    std::string backup_ip;

    int sock_fd;

    int max_wait_time;

    int send_pack_num;
    int recv_pack_num;
    int lost_pack_num;

    struct sockaddr_in send_addr;
    struct sockaddr_in recv_addr;

    char send_pack[PACK_SIZE];
    char recv_pack[PACK_SIZE + IP_HEADER_SIZE];

    struct timeval first_send_time;
    struct timeval recv_time;

    double min_time;
    double max_time;
    double sum_time;

    unsigned short pid = 0;

};
//  iping.cpp
#include "iping.h"
#include "iostream"

Ping::Ping() {
    this->send_pack_num = 10;
    this->recv_pack_num = 0;
    this->lost_pack_num = 0;

    this->min_time = 0;
    this->max_time = 60 * 1000;
    this->sum_time = 0;

    this->input_domain = "127.0.0.1";

}

Ping::~Ping() {
  cout << "1243" << endl;
    if(close(sock_fd) == -1) {
        fprintf(stderr, "Close socket error:%s \n\a", strerror(errno));
        exit(1);
    }
}

unsigned short Ping::CheckSum(unsigned short * header, int length) {
    int check_sum = 0;
    int nleft = length;
    unsigned short * p = header;
    while(nleft > 1){
        check_sum += *p++;
        nleft -= sizeof(unsigned short);
    }

    if (nleft){
        check_sum += *(unsigned char*)p;
    }

    check_sum = (check_sum >> 16) + (check_sum & 0xffff);
    check_sum += (check_sum >> 16);
    return (unsigned short)(~check_sum);
}


void Ping::CreateSocket() {
    struct protoent * protocol;
    unsigned long in_addr;
    struct hostent *host_pointer;

    if((protocol = getprotobyname("icmp")) == NULL){
        fprintf(stderr, "Get protocol error:%s \n\a", strerror(errno));
        exit(1);
    }
    // MACOS use SOCK_DGRAM
    #ifdef __APPLE__
      if((sock_fd = socket(AF_INET, SOCK_DGRAM, protocol->p_proto)) == -1){
        fprintf(stderr, "Greate RAW socket error:%s \n\a", strerror(errno));
        exit(1);
      }
    #endif
    send_addr.sin_family = AF_INET;

    if((in_addr = inet_addr(input_domain.c_str())) == INADDR_NONE){
        host_pointer = gethostbyname(input_domain.c_str());
        if(host_pointer == NULL){
            fprintf(stderr, "Get host by name error:%s \n\a", strerror(errno));
            exit(1);
        } else{
            this->send_addr.sin_addr.s_addr = (*(struct in_addr *)host_pointer->h_addr).s_addr;
        }
    } else{
        this->send_addr.sin_addr.s_addr = in_addr;
    }
    this->backup_ip = inet_ntoa(send_addr.sin_addr);
    gettimeofday(&first_send_time, NULL);
}

int Ping::GeneratePacket()
{
    int pack_size;
    ICMP_HEADER *icmp_pointer;
    struct timeval time_pointer;
    gettimeofday(&time_pointer, NULL);
    pack_size = PACK_SIZE;

    memset(send_pack,0,sizeof(send_pack));
    memset(recv_pack,0,sizeof(recv_pack));

    icmp_pointer = (ICMP_HEADER *)send_pack;

    icmp_pointer->icmp_type = ICMP_ECHO;
    icmp_pointer->icmp_code = 0;
    icmp_pointer->icmp_seq = send_pack_num + 1;
    icmp_pointer->icmp_id = getpid();
    icmp_pointer->timestamp_s = time_pointer.tv_sec;
    icmp_pointer->timestamp_us = time_pointer.tv_usec;
    icmp_pointer->icmp_checksum = CheckSum((unsigned short *)send_pack, pack_size);
    return pack_size;
}

void Ping::SendPacket() {
    int pack_size = this->GeneratePacket();
    if((sendto(sock_fd, send_pack, pack_size, 0, (const struct sockaddr *)&send_addr, sizeof(send_addr))) < 0){
        fprintf(stderr, "Sendto error:%s \n\a", strerror(errno));
        exit(1);
    }
    this->send_pack_num++;
}

int Ping::ResolvePacket(int pack_size) {
    int icmp_len, ip_header_len;
    IP_HEADER *ip_pointer = (IP_HEADER *)recv_pack;
    double rtt;
    unsigned long time_send;

    ip_header_len = ip_pointer->header_length << 2;
    ICMP_HEADER *icmp_pointer = (ICMP_HEADER *)(recv_pack + ip_header_len);
    icmp_len = pack_size - ip_header_len;


    if(icmp_len < 8) {
        printf("received ICMP pack length:%d(%d) is error!\n", pack_size, icmp_len);
        lost_pack_num++;
        return -1;
    }

    if((icmp_pointer->icmp_type == ICMP_ECHO_REPLY) && (backup_ip == inet_ntoa(recv_addr.sin_addr))){
        struct timeval time_pointer_end;
        gettimeofday(&time_pointer_end, NULL);
        unsigned long time_send_s = icmp_pointer->timestamp_s;
        unsigned long time_send_us = icmp_pointer->timestamp_us;

        if(time_pointer_end.tv_usec - time_send_us < 0) {
            --time_pointer_end.tv_sec;
          time_pointer_end.tv_usec += 10000000;
        }
        rtt = (time_pointer_end.tv_sec - time_send_s) * 1000 + (double)time_pointer_end.tv_usec / 1000.0;

        printf("%d byte from %s : icmp_seq=%u ttl=%d time=%.1fms\n",
               icmp_len,
               inet_ntoa(recv_addr.sin_addr),
               icmp_pointer->icmp_seq,
               ip_pointer->ttl,
               rtt);

        recv_pack_num++;
    } else{
        if(close(sock_fd) == -1) {
          fprintf(stderr, "Close socket error:%s \n\a", strerror(errno));
          exit(1);
        }
      sock_fd = 0;
    }

}

void Ping::RecvPacket() {
    int recv_size, fromlen;
    fromlen = sizeof(struct sockaddr);
    if((recv_size = recvfrom(sock_fd, recv_pack, sizeof(recv_pack),
                             0, (struct sockaddr *)&recv_addr, (socklen_t *)&fromlen)) < 0) {
      fprintf(stderr, "packet error(size:%d):%s \n\a", recv_size, strerror(errno));
      lost_pack_num++;
    } else{
      ResolvePacket(recv_size);
    }
}
// main.cpp
#include "iping.cpp"
int main() {
    Ping ping;
    ping.CreateSocket();
    ping.SendPacket();
    ping.RecvPacket();
}

Result

image

@southorange1228
Copy link
Author

@southorange1228 southorange1228 commented Dec 30, 2021

There is node-addon-api project.

// ping.h
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <iostream>

#include "napi.h"

using namespace std;

#define PACK_SIZE 32
#define IP_HEADER_SIZE 20
#define ICMP_ECHO 0
#define ICMP_ECHO_REPLY 8
#define ICMP_REPLY_CODE 0

struct PingOptions {
    string addr;
    int timeout;
    int retry;
};

struct IP_HEADER {
    unsigned char header_length : 4;  // header of length
    unsigned char version : 4;        // version
    unsigned char tos;                // type of service
    unsigned short total_length;      // total length of packet
    unsigned short identifier;        // id
    unsigned short frag_and_flags;    // fragment and flag
    unsigned char ttl;                // ttl
    unsigned char protocol;           // protocol eg: TCP UDP etc.
    unsigned short checksum;          // checksum
    unsigned long source_ip;          // source ip
    unsigned long dest_ip;            // destination ip
};

struct ICMP_HEADER {
    unsigned char icmp_type;
    unsigned char icmp_code;
    unsigned short icmp_checksum;
    unsigned short icmp_id;
    unsigned short icmp_seq;
    unsigned long timestamp_s;
    unsigned long timestamp_us;
};

class Ping : public Napi::ObjectWrap<Ping> {
public:
    static Napi::Object Init(Napi::Env env, Napi::Object exports);
    Ping(const Napi::CallbackInfo &info);
    ~Ping();

private:
    Napi::Value start(const Napi::CallbackInfo &info);

    unsigned short CheckSum(unsigned short *header, int length);
    void CreateSocket();

    void SendPacket();
    void RecvPacket();

    int GeneratePacket();
    int ResolvePacket(int pack_size);
    unsigned short GetPid();

    PingOptions options;

    std::string backup_ip;     

    int sock_fd;

    int send_pack_num;  
    int recv_pack_num;  
    int lost_pack_num;  

    struct sockaddr_in send_addr;  
    struct sockaddr_in recv_addr;  

    char send_pack[PACK_SIZE];                   
    char recv_pack[PACK_SIZE + IP_HEADER_SIZE];  

    struct timeval first_send_time;  
    struct timeval recv_time;        

    Napi::Env env = NULL;
};
// ping.cpp
#include "ping.h"

Napi::Object Ping::Init(Napi::Env env, Napi::Object exports) {
    Napi::Function func = DefineClass(env, "Ping",
                                      {
                                          InstanceMethod("start", &Ping::start),
                                      });

    Napi::FunctionReference *constructor = new Napi::FunctionReference();
    *constructor = Napi::Persistent(func);
    env.SetInstanceData(constructor);

    exports.Set("Ping", func);
    return exports;
}

Ping::Ping(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Ping>(info) {
    Napi::Env env = info.Env();
    this->env = env;

    int length = info.Length();

    if (length <= 0) {
        Napi::TypeError::New(env, "Option should provide").ThrowAsJavaScriptException();
        return;
    }
    if (!info[0].IsObject()) {
        Napi::TypeError::New(env, "Option should be object").ThrowAsJavaScriptException();
        return;
    }
    Napi::Object obj = info[0].ToObject();
    if (obj.Has("addr") && obj.Get("addr").IsString()) {
        string originAddr = obj.Get("addr").As<Napi::String>();
        this->options.addr = originAddr;
    } else {
        Napi::TypeError::New(env, "addr should be string").ThrowAsJavaScriptException();
        return;
    }
    if (obj.Has("retry") && obj.Get("retry").IsNumber()) {
        this->options.retry = obj.Get("retry").As<Napi::Number>();
    } else {
        Napi::TypeError::New(env, "retry should be number").ThrowAsJavaScriptException();
        return;
    }
    if (obj.Has("timeout")) {
        if (obj.Get("timeout").IsNumber()) {
            this->options.timeout = obj.Get("timeout").As<Napi::Number>();
        } else {
            Napi::TypeError::New(env, "retry should be number").ThrowAsJavaScriptException();
            return;
        }
    } else {
        this->options.timeout = 60 * 1000;
    }
    this->send_pack_num = 0;
    this->recv_pack_num = 0;
    this->lost_pack_num = 0;
}

Ping::~Ping() {
    if (close(sock_fd) == -1) {
        fprintf(stderr, "Close socket error:%s \n\a", strerror(errno));
        Napi::TypeError::New(env, "Close socket Error").ThrowAsJavaScriptException();
        exit(1);
    }
}

unsigned short Ping::CheckSum(unsigned short *header, int length) {
    int check_sum = 0;           
    int nleft = length;         
    unsigned short *p = header;  

    while (nleft > 1) {
        check_sum += *p++;  
        nleft -= sizeof(unsigned short);
    }

    if (nleft) {
        check_sum += *(unsigned char *)p;
    }

    check_sum = (check_sum >> 16) + (check_sum & 0xffff);  
    check_sum += (check_sum >> 16);                        

    return (unsigned short)(~check_sum);
}

unsigned short Ping::GetPid() {
    unsigned short pid = getpid();
    // MACOS pid will be more than short
#ifdef __APPLE__
    pid = getpid() >> 1;
#endif
    return pid;
}

void Ping::CreateSocket() {
    struct protoent *protocol;
    unsigned long in_addr;
    struct hostent *host_pointer;

    if ((protocol = getprotobyname("icmp")) == NULL) {
        fprintf(stderr, "Get protocol error:%s \n\a", strerror(errno));
        exit(1);
    }
// MACOS use SOCK_DGRAM
#ifdef __APPLE__
    if ((sock_fd = socket(AF_INET, SOCK_DGRAM, protocol->p_proto)) == -1) {
        fprintf(stderr, "Create RAW socket error:%s \n\a", strerror(errno));
        exit(1);
    }
#endif
    send_addr.sin_family = AF_INET;
    if ((in_addr = inet_addr(options.addr.c_str())) == INADDR_NONE) {
        host_pointer = gethostbyname(options.addr.c_str());
        if (host_pointer == NULL) {
            fprintf(stderr, "Get host by name error:%s \n\a", strerror(errno));
            exit(1);
        } else {
            this->send_addr.sin_addr.s_addr = (*(struct in_addr *)host_pointer->h_addr).s_addr;
        }
    } else {
        this->send_addr.sin_addr.s_addr = in_addr;
    }
    this->backup_ip = inet_ntoa(send_addr.sin_addr);
    gettimeofday(&first_send_time, NULL);
}

int Ping::GeneratePacket() {
    int pack_size;
    ICMP_HEADER *icmp_pointer;
    struct timeval time_pointer;
    gettimeofday(&time_pointer, NULL);
    pack_size = PACK_SIZE;

    memset(send_pack, 0, sizeof(send_pack));
    memset(recv_pack, 0, sizeof(recv_pack));

    icmp_pointer = (ICMP_HEADER *)send_pack;

    icmp_pointer->icmp_type = ICMP_ECHO;
    icmp_pointer->icmp_code = 0;
    icmp_pointer->icmp_seq = send_pack_num + 1;
    icmp_pointer->icmp_id = this->GetPid();
    icmp_pointer->timestamp_s = time_pointer.tv_sec;
    icmp_pointer->timestamp_us = time_pointer.tv_usec;
    icmp_pointer->icmp_checksum = this->CheckSum((unsigned short *)send_pack, pack_size);
    return pack_size;
}

void Ping::SendPacket() {
    int pack_size = this->GeneratePacket();
    if((sendto(sock_fd, send_pack, pack_size, 0, (const struct sockaddr *)&send_addr, sizeof(send_addr))) < 0){
        fprintf(stderr, "Sendto error:%s \n\a", strerror(errno));
        exit(1);
    }
    this->send_pack_num++;
}

int Ping::ResolvePacket(int pack_size) {
    int icmp_len, ip_header_len;
    ICMP_HEADER *icmp_pointer;
    IP_HEADER *ip_pointer = (IP_HEADER *)recv_pack;
    double rtt;
    struct timeval time_end;

    gettimeofday(&time_end, NULL);

    ip_header_len = ip_pointer->header_length << 2;             
    icmp_pointer = (ICMP_HEADER *)(recv_pack + ip_header_len);  
    icmp_len = pack_size - ip_header_len;                      

    //收到的ICMP包长度小于报头
    if (icmp_len < 8) {
        printf("received ICMP pack length:%d(%d) is error!\n", pack_size, icmp_len);
        lost_pack_num++;
        return -1;
    }

    if ((icmp_pointer->icmp_type == ICMP_ECHO_REPLY) && (backup_ip == inet_ntoa(recv_addr.sin_addr)) &&
        (icmp_pointer->icmp_code == ICMP_REPLY_CODE)) {
        unsigned long time_send_s = icmp_pointer->timestamp_s;
        unsigned long time_send_us = icmp_pointer->timestamp_us;
        if ((recv_time.tv_usec - time_send_us) < 0) {
            --recv_time.tv_sec;
            recv_time.tv_usec += 10000000;
        }
        rtt = (recv_time.tv_sec - time_send_s) * 1000 + (double)recv_time.tv_usec / 1000.0;

        printf("%d byte from %s : icmp_seq=%u ttl=%d time=%.1fms\n", icmp_len, inet_ntoa(recv_addr.sin_addr),
               icmp_pointer->icmp_seq, ip_pointer->ttl, rtt);
        recv_pack_num++;
        return 0;
    } else {
        lost_pack_num++;
        return -1;
    }
}

void Ping::RecvPacket() {
    int recv_size, fromlen;
    fromlen = sizeof(struct sockaddr);
    if ((recv_size = recvfrom(sock_fd, recv_pack, sizeof(recv_pack), 0, (struct sockaddr *)&recv_addr,
                              (socklen_t *)&fromlen)) < 0) {
        fprintf(stderr, "packet error(size:%d):%s \n\a", recv_size, strerror(errno));
        lost_pack_num++;
    } else {
        gettimeofday(&recv_time, NULL);
        ResolvePacket(recv_size);
    }
}

Napi::Value Ping::start(const Napi::CallbackInfo &info) {
    Napi::Env env = info.Env();
    CreateSocket();
    SendPacket();
    RecvPacket();
    return Napi::Number::New(env, 100);
}
// init.cc
#include "ping.cpp"

Napi::Object Init(Napi::Env env, Napi::Object exports) {
    Ping::Init(env, exports);
    return exports;
}

NODE_API_MODULE(ping, Init)
// test.js
'use strict';

const ping = require('bindings')('ping');

const a = new ping.Ping({
    addr: '127.0.0.1',
    retry: 10,
    timeout: 10
})
a.start()

Result

image

@southorange1228
Copy link
Author

@southorange1228 southorange1228 commented Jan 4, 2022

Hi, @NickNaso ,
Could you find some errors?

@github-actions
Copy link

@github-actions github-actions bot commented Apr 5, 2022

This issue is stale because it has been open many days with no activity. It will be closed soon unless the stale label is removed or a comment is made.

@github-actions github-actions bot added the stale label Apr 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants
X Tutup