Search code examples
pythonc++qtsocketsqlocalsocket

C++ behaves differently in Qt, bind() returns EINVAL


I have already checked out bind() return EINVAL and that is not the issue here. Please read through before you get furious about duplication.

I am trying to connect to wpa_supplicant. Basically I'm trying to achieve this using C++:

import os
import select
import socket

interface = "wlp4s0"
wpa_send_path = "/run/wpa_supplicant/"+interface
wpa_recv_path = "/tmp/wpa_ctrl_{pid}-{count}".format(pid=os.getpid(), count=1)

soc = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
soc.bind(wpa_recv_path)
soc.connect(wpa_send_path)

print("> PING")
soc.send(b"PING")
print("<", soc.recv(4096).decode().strip())

from https://gist.github.com/artizirk/cd3980c8ff870eb0bfce68bc26a2676b

And I have done what I wanted but using plain C++. This is the code:

#include <iostream>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>

#define PATH   "/run/wpa_supplicant/wlp4s0"
char path[100] = "/tmp/wpa_ctrl_";

int main(void) {

  int ctrl_socket;
  char buffer[1024];
  struct sockaddr_un socket_addr, send_addr;

  memset(&socket_addr, 0, sizeof(struct sockaddr_un));
  memset(&send_addr, 0, sizeof(struct sockaddr_un));

  socket_addr.sun_family = AF_UNIX;
  send_addr.sun_family = AF_UNIX;

  strcpy(send_addr.sun_path, PATH);

  strcat(path, std::to_string(getpid()).c_str());
  strcat(path, "-1");
  strcat(socket_addr.sun_path, path);

  if ((ctrl_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
    std::cerr << "Error creating socket!" << std::endl;
    std::exit(EXIT_FAILURE);
  }

  /* Unlink if already bound */
  unlink(socket_addr.sun_path);

  if ((connect(ctrl_socket, (struct sockaddr *)&send_addr, SUN_LEN(&send_addr))) == -1) {
    std::cerr << "Error connecting to socket!" << std::endl;
    std::exit(EXIT_FAILURE);
  }

  if (bind(ctrl_socket, (const struct sockaddr *)&socket_addr, offsetof(struct sockaddr_un, sun_path) + strlen(path) + 1) == -1) {
        perror("Error");
        exit(EXIT_FAILURE);
  }

  send(ctrl_socket, "PING", 5, 0);
  recv(ctrl_socket, buffer, 1024, 0);

  std::cout << buffer << std::endl;

  close(ctrl_socket);

  return 0;
}

This code works fine. But when I do it in Qt, bind() always return EINVAL i.e Invalid Arguments. Here's that code:

WPASupplicantControl::WPASupplicantControl(std::string wlan_interface_name)
    :wpa_send_ctrl_iface(WPA_SEND_CTRL_IFACE_PREFIX + QString::fromStdString(wlan_interface_name)),
      wpa_recv_ctrl_iface(
          WPA_RECV_CTRL_IFACE_PREFIX +
          QString::fromStdString(std::to_string(getpid())) +
          "-1"
          )
{
    struct sockaddr_un send_address, recv_address;

    send_address.sun_family = AF_UNIX;
    recv_address.sun_family = AF_UNIX;

    memset(&send_address, 0, sizeof (send_address));
    memset(&recv_address, 0, sizeof (recv_address));

    strncpy(send_address.sun_path, wpa_send_ctrl_iface.toStdString().c_str(), wpa_send_ctrl_iface.length());
    strncpy(recv_address.sun_path, wpa_recv_ctrl_iface.toStdString().c_str(), wpa_send_ctrl_iface.length());

    if ((wpa_control_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
        qCritical() << "socket() failed!";
        exit(EXIT_FAILURE);
    }

    /* Attatch to sending and receiving control interfaces */
    if (connect(wpa_control_socket, (const struct sockaddr *)&send_address, SUN_LEN(&send_address)) == -1) {
        qCritical() << "Error connecting to wpa_supplicant send control iface!";
        close(wpa_control_socket);
        exit(EXIT_FAILURE);
    }

    /* Detatch if it's already bound */
    unlink(recv_address.sun_path);

    if (bind(wpa_control_socket, (const struct sockaddr *)&recv_address, offsetof(struct sockaddr_un, sun_path) + wpa_recv_ctrl_iface.length() + 1) == -1) {
        qCritical() << "Error binding to wpa_supplicant recv control iface!";
        close(wpa_control_socket);
        exit(EXIT_FAILURE);
    }
}

A functional dummy project to test this code is at: https://github.com/gaurav712/socket_test

I have checked all the values hundreds of times and they're all fine but still it doesn't work. It just goes upto that connect() call.

I thought I should use something Qt-specific and I found out about QLocalSocket https://doc.qt.io/qt-5/qlocalsocket.html but when I import it, Qt doesn't recognize that class. Maybe its because I'm on Qt 6.x and it is now deprecated(although I didn't find any official deprecation notice thingy). What do I do? What am I doing wrong?


Solution

  • Absolutely nothing to do with Qt: You do a memset to all-0 of the sockaddr structures after you've set the socket's family to AF_UNIX (which is "1" on most platforms). How should the system know you want a local socket?