summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTill Kamppeter <till.kamppeter@gmail.com>2016-12-15 21:50:12 -0200
committerTill Kamppeter <till.kamppeter@gmail.com>2016-12-15 21:50:12 -0200
commit551cb748946137eab644199eca459d4e320c91cb (patch)
tree58f8e402051d0d71eb64e851b3fd23316da67f9f
parentb1d6e63268a93fdc46e2b868d03f3b5028d08151 (diff)
Let ippusbxd advertise the connected printer via Bonjour
To let software like ippfind or cups-browsed find an IPP-over-USB printer we broadcast the printer which we are mapping to a network interface via Bonjour (using Avahi). As Avahi is not able to broadcast services on the loopback interface (as the loopback interface does not support multicast), we allow supplying any other interface on the command line, making the printer socket be bound there and then the broadcast done on this interface. To make the printer still appearing only on the local machine it is recommended to use the dummy interface, setting it up via sudo modprobe dummy and one of sudo ifconfig dummy0 10.0.0.1 netmask 255.255.255.0 multicast or sudo ifconfig dummy0 up multicast After that one runs ippusbxd with the "-i dummy0" option and the printer is available on the desired port on 10.0.0.1.
-rw-r--r--src/CMakeLists.txt10
-rw-r--r--src/FindAVAHICLIENT.cmake26
-rw-r--r--src/FindAVAHICOMMON.cmake26
-rw-r--r--src/bonjour.c305
-rw-r--r--src/bonjour.h31
-rw-r--r--src/ippusbxd.c41
-rw-r--r--src/options.h2
-rw-r--r--src/tcp.c48
-rw-r--r--src/tcp.h4
-rw-r--r--src/usb.c68
-rw-r--r--src/usb.h1
11 files changed, 550 insertions, 12 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7074c86..dd7bbd5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -15,11 +15,16 @@ elseif( ${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format-nonliteral")
endif()
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR})
+
# Pthreads
find_package(Threads REQUIRED)
+# Avahi
+find_package(AVAHICOMMON REQUIRED)
+find_package(AVAHICLIENT REQUIRED)
+
# Libusb
-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR})
find_package(LIBUSB REQUIRED)
include_directories(${LIBUSB_INCLUDE_DIR})
@@ -31,7 +36,10 @@ tcp.c
usb.c
logging.c
options.c
+bonjour.c
)
target_link_libraries(ippusbxd ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(ippusbxd ${LIBUSB_LIBRARIES})
+target_link_libraries(ippusbxd ${AVAHICOMMON_LIBRARIES})
+target_link_libraries(ippusbxd ${AVAHICLIENT_LIBRARIES})
diff --git a/src/FindAVAHICLIENT.cmake b/src/FindAVAHICLIENT.cmake
new file mode 100644
index 0000000..2d2f815
--- /dev/null
+++ b/src/FindAVAHICLIENT.cmake
@@ -0,0 +1,26 @@
+# Credit to chdromiumos project
+
+# - Try to find the freetype library
+# Once done this defines
+#
+# AVAHICLIENT_FOUND - system has libusb
+# AVAHICLIENT_INCLUDE_DIR - the libusb include directory
+# AVAHICLIENT_LIBRARIES - Link these to use libusb
+# Copyright (c) 2006, 2008 Laurent Montel, <montel@kde.org>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+if (AVAHICLIENT_INCLUDE_DIR AND AVAHICLIENT_LIBRARIES)
+ # in cache already
+ set(AVAHICLIENT_FOUND TRUE)
+else (AVAHICLIENT_INCLUDE_DIR AND AVAHICLIENT_LIBRARIES)
+ FIND_PATH(AVAHICLIENT_INCLUDE_DIR client.h
+ PATHS ${PC_AVAHICLIENT_INCLUDEDIR} ${PC_AVAHICLIENT_INCLUDE_DIRS})
+ FIND_LIBRARY(AVAHICLIENT_LIBRARIES NAMES avahi-client
+ PATHS ${PC_AVAHICLIENT_LIBDIR} ${PC_AVAHICLIENT_LIBRARY_DIRS})
+
+ #include(FindPackageHandleStandardArgs)
+ #FIND_PACKAGE_HANDLE_STANDARD_ARGS(AVAHICLIENT DEFAULT_MSG AVAHICLIENT_LIBRARIES AVAHICLIENT_INCLUDE_DIR)
+
+ MARK_AS_ADVANCED(AVAHICLIENT_INCLUDE_DIR AVAHICLIENT_LIBRARIES)
+endif (AVAHICLIENT_INCLUDE_DIR AND AVAHICLIENT_LIBRARIES)
diff --git a/src/FindAVAHICOMMON.cmake b/src/FindAVAHICOMMON.cmake
new file mode 100644
index 0000000..7637807
--- /dev/null
+++ b/src/FindAVAHICOMMON.cmake
@@ -0,0 +1,26 @@
+# Credit to chdromiumos project
+
+# - Try to find the freetype library
+# Once done this defines
+#
+# AVAHICOMMON_FOUND - system has libusb
+# AVAHICOMMON_INCLUDE_DIR - the libusb include directory
+# AVAHICOMMON_LIBRARIES - Link these to use libusb
+# Copyright (c) 2006, 2008 Laurent Montel, <montel@kde.org>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+if (AVAHICOMMON_INCLUDE_DIR AND AVAHICOMMON_LIBRARIES)
+ # in cache already
+ set(AVAHICOMMON_FOUND TRUE)
+else (AVAHICOMMON_INCLUDE_DIR AND AVAHICOMMON_LIBRARIES)
+ FIND_PATH(AVAHICOMMON_INCLUDE_DIR thread-watch.h
+ PATHS ${PC_AVAHICOMMON_INCLUDEDIR} ${PC_AVAHICOMMON_INCLUDE_DIRS})
+ FIND_LIBRARY(AVAHICOMMON_LIBRARIES NAMES avahi-common
+ PATHS ${PC_AVAHICOMMON_LIBDIR} ${PC_AVAHICOMMON_LIBRARY_DIRS})
+
+ #include(FindPackageHandleStandardArgs)
+ #FIND_PACKAGE_HANDLE_STANDARD_ARGS(AVAHICOMMON DEFAULT_MSG AVAHICOMMON_LIBRARIES AVAHICOMMON_INCLUDE_DIR)
+
+ MARK_AS_ADVANCED(AVAHICOMMON_INCLUDE_DIR AVAHICOMMON_LIBRARIES)
+endif (AVAHICOMMON_INCLUDE_DIR AND AVAHICOMMON_LIBRARIES)
diff --git a/src/bonjour.c b/src/bonjour.c
new file mode 100644
index 0000000..f685f49
--- /dev/null
+++ b/src/bonjour.c
@@ -0,0 +1,305 @@
+/* Copyright (C) 2014 Daniel Dressler and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <net/if.h>
+
+#include "bonjour.h"
+#include "logging.h"
+
+/*
+ * 'dnssd_callback()' - Handle Bonjour registration events.
+ */
+
+static void
+dnssd_callback(
+ AvahiEntryGroup *srv, /* I - Service */
+ AvahiEntryGroupState state, /* I - Registration state */
+ void *context) /* I - Printer */
+{
+ (void)srv;
+ (void)state;
+ (void)context;
+}
+
+
+/*
+ * 'dnssd_client_cb()' - Client callback for Avahi.
+ *
+ * Called whenever the client or server state changes...
+ */
+
+static void
+dnssd_client_cb(
+ AvahiClient *c, /* I - Client */
+ AvahiClientState state, /* I - Current state */
+ void *userdata) /* I - User data (unused) */
+{
+ (void)userdata;
+
+ if (!c)
+ return;
+
+ switch (state)
+ {
+ default :
+ fprintf(stderr, "Ignore Avahi state %d.\n", state);
+ break;
+
+ case AVAHI_CLIENT_FAILURE:
+ if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
+ {
+ fputs("Avahi server crashed, exiting.\n", stderr);
+ exit(1);
+ }
+ break;
+ }
+}
+
+void
+dnssd_init(bonjour_t *bonjour_data)
+{
+ int error; /* Error code, if any */
+
+ if ((bonjour_data->DNSSDMaster = avahi_threaded_poll_new()) == NULL)
+ {
+ fputs("Error: Unable to initialize Bonjour.\n", stderr);
+ exit(1);
+ }
+
+ if ((bonjour_data->DNSSDClient =
+ avahi_client_new(avahi_threaded_poll_get(bonjour_data->DNSSDMaster),
+ AVAHI_CLIENT_NO_FAIL,
+ dnssd_client_cb, NULL, &error)) == NULL)
+ {
+ fputs("Error: Unable to initialize Bonjour.\n", stderr);
+ exit(1);
+ }
+
+ avahi_threaded_poll_start(bonjour_data->DNSSDMaster);
+}
+
+void
+dnssd_shutdown(bonjour_t *bonjour_data) {
+ avahi_threaded_poll_lock(bonjour_data->DNSSDMaster);
+ if (bonjour_data->ipp_ref)
+ avahi_entry_group_free(bonjour_data->ipp_ref);
+ avahi_threaded_poll_unlock(bonjour_data->DNSSDMaster);
+}
+
+/*
+ * 'register_printer()' - Register a printer object via Bonjour.
+ */
+
+int
+register_printer(bonjour_t *bonjour_data,
+ char *device_id,
+ char *interface,
+ int port)
+{
+ AvahiStringList *ipp_txt; /* Bonjour IPP TXT record */
+ char temp[256]; /* Subtype service string */
+ char dnssd_name[1024];
+ char *dev_id = NULL;
+ const char *make; /* I - Manufacturer */
+ const char *model; /* I - Model name */
+ const char *serial = NULL;
+ const char *cmd;
+ int pwgraster = 0,
+ appleraster = 0,
+ pdf = 0,
+ jpeg = 0;
+ char formats[1024]; /* I - Supported formats */
+ char *ptr;
+ int error;
+
+ /*
+ * Parse the device ID for MFG, MDL, and CMD
+ */
+
+ dev_id = strdup(device_id);
+ if ((ptr = strcasestr(dev_id, "MFG:")) == NULL)
+ if ((ptr = strcasestr(dev_id, "MANUFACTURER:")) == NULL) {
+ ERR("No manufacturer info in device ID");
+ free(dev_id);
+ return -1;
+ }
+ make = strchr(ptr, ':') + 1;
+ if ((ptr = strcasestr(dev_id, "MDL:")) == NULL)
+ if ((ptr = strcasestr(dev_id, "MODEL:")) == NULL) {
+ ERR("No model info in device ID");
+ free(dev_id);
+ return -1;
+ }
+ model = strchr(ptr, ':') + 1;
+ if ((ptr = strcasestr(dev_id, "SN:")) == NULL)
+ if ((ptr = strcasestr(dev_id, "SERN:")) == NULL) {
+ if ((ptr = strcasestr(dev_id, "SERIALNUMBER:")) == NULL) {
+ NOTE("No serial number info in device ID");
+ }
+ }
+ if (ptr)
+ serial = strchr(ptr, ':') + 1;
+ if ((ptr = strcasestr(dev_id, "CMD:")) == NULL)
+ if ((ptr = strcasestr(dev_id, "COMMAND SET:")) == NULL) {
+ ERR("No page description language info in device ID");
+ free(dev_id);
+ return -1;
+ }
+ cmd = strchr(ptr, ':') + 1;
+ ptr = strchr(make, ';');
+ if (ptr) *ptr = '\0';
+ ptr = strchr(model, ';');
+ if (ptr) *ptr = '\0';
+ if (serial) {
+ ptr = strchr(serial, ';');
+ if (ptr) *ptr = '\0';
+ }
+ ptr = strchr(cmd, ';');
+ if (ptr) *ptr = '\0';
+
+ if ((ptr = strcasestr(cmd, "pwg")) != NULL &&
+ (ptr = strcasestr(ptr, "raster")) != NULL)
+ pwgraster = 1;
+ if (((ptr = strcasestr(cmd, "apple")) != NULL &&
+ (ptr = strcasestr(ptr, "raster")) != NULL) ||
+ ((ptr = strcasestr(cmd, "urf")) != NULL))
+ appleraster = 1;
+ if ((ptr = strcasestr(cmd, "pdf")) != NULL)
+ pdf = 1;
+ if ((ptr = strcasestr(cmd, "jpeg")) != NULL ||
+ (ptr = strcasestr(cmd, "jpg")) != NULL)
+ jpeg = 1;
+ snprintf(formats, sizeof(formats),"%s%s%s%s",
+ (pdf ? "application/pdf," : ""),
+ (pwgraster ? "image/pwg-raster," : ""),
+ (appleraster ? "image/urf," : ""),
+ (jpeg ? "image/jpeg," : ""));
+ formats[strlen(formats) - 1] = '\0';
+
+ /*
+ * Additional printer properties
+ */
+
+ snprintf(temp, sizeof(temp), "http://localhost:%d/", port);
+ if (serial)
+ snprintf(dnssd_name, sizeof(dnssd_name), "%s [%s]", model, serial);
+ else
+ snprintf(dnssd_name, sizeof(dnssd_name), "%s", model);
+
+ /*
+ * Create the TXT record...
+ */
+
+ ipp_txt = NULL;
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "rp=ipp/print");
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "ty=%s %s", make, model);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "adminurl=%s", temp);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "product=(%s)", model);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "pdl=%s", formats);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "Color=U");
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "Duplex=U");
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "usb_MFG=%s", make);
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "usb_MDL=%s", model);
+ if (appleraster)
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "URF=CP1,IS1-5-7,MT1-2-3-4-5-6-8-9-10-11-12-13,RS300,SRGB24,V1.4,W8,DM1");
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "priority=60");
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "txtvers=1");
+ ipp_txt = avahi_string_list_add_printf(ipp_txt, "qtotal=1");
+ free(dev_id);
+
+ avahi_threaded_poll_lock(bonjour_data->DNSSDMaster);
+
+ /*
+ * Register _printer._tcp (LPD) with port 0 to reserve the service name...
+ */
+
+ bonjour_data->ipp_ref = avahi_entry_group_new(bonjour_data->DNSSDClient,
+ dnssd_callback, NULL);
+ error =
+ avahi_entry_group_add_service_strlst(bonjour_data->ipp_ref,
+ (interface ?
+ (int)if_nametoindex(interface) :
+ AVAHI_IF_UNSPEC),
+ AVAHI_PROTO_UNSPEC, 0,
+ dnssd_name,
+ "_printer._tcp", NULL, NULL, 0,
+ NULL);
+ if (error)
+ ERR("Error registering %s as Unix printer (_printer._tcp): %d", dnssd_name,
+ error);
+
+ /*
+ * Then register the _ipp._tcp (IPP)...
+ */
+
+ error =
+ avahi_entry_group_add_service_strlst(bonjour_data->ipp_ref,
+ (interface ?
+ (int)if_nametoindex(interface) :
+ AVAHI_IF_UNSPEC),
+ AVAHI_PROTO_UNSPEC, 0,
+ dnssd_name,
+ "_ipp._tcp", NULL, NULL, port,
+ ipp_txt);
+ if (error)
+ ERR("Error registering %s as IPP printer (_ipp._tcp): %d", dnssd_name,
+ error);
+
+ /*
+ * Finally _http.tcp (HTTP) for the web interface...
+ */
+
+ error =
+ avahi_entry_group_add_service_strlst(bonjour_data->ipp_ref,
+ (interface ?
+ (int)if_nametoindex(interface) :
+ AVAHI_IF_UNSPEC),
+ AVAHI_PROTO_UNSPEC, 0,
+ dnssd_name,
+ "_http._tcp", NULL, NULL, port,
+ NULL);
+ if (error)
+ ERR("Error registering web interface of %s (_http._tcp): %d", dnssd_name,
+ error);
+ else {
+ error =
+ avahi_entry_group_add_service_subtype(bonjour_data->ipp_ref,
+ (interface ?
+ (int)if_nametoindex(interface) :
+ AVAHI_IF_UNSPEC),
+ AVAHI_PROTO_UNSPEC, 0,
+ dnssd_name,
+ "_http._tcp", NULL,
+ "_printer._sub._http._tcp");
+ if (error)
+ ERR("Error registering subtype for web interface of %s (_printer._sub._http._tcp): %d", dnssd_name,
+ error);
+ }
+
+ /*
+ * Commit it...
+ */
+
+ avahi_entry_group_commit(bonjour_data->ipp_ref);
+ avahi_threaded_poll_unlock(bonjour_data->DNSSDMaster);
+
+ avahi_string_list_free(ipp_txt);
+
+ return 0;
+}
+
diff --git a/src/bonjour.h b/src/bonjour.h
new file mode 100644
index 0000000..95314fc
--- /dev/null
+++ b/src/bonjour.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2014 Daniel Dressler and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License. */
+
+#pragma once
+
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-common/error.h>
+#include <avahi-common/thread-watch.h>
+
+typedef struct bonjour_s {
+ AvahiThreadedPoll *DNSSDMaster;
+ AvahiClient *DNSSDClient;
+ AvahiEntryGroup *ipp_ref;
+} bonjour_t;
+
+void dnssd_init(bonjour_t *bonjour_data);
+void dnssd_shutdown(bonjour_t *bonjour_data);
+int register_printer(bonjour_t *bonjour_data,
+ char *device_id, char *interface, int port);
diff --git a/src/ippusbxd.c b/src/ippusbxd.c
index 60c0668..5005110 100644
--- a/src/ippusbxd.c
+++ b/src/ippusbxd.c
@@ -12,9 +12,11 @@
* See the License for the specific language governing permissions and
* limitations under the License. */
+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
+#include <string.h>
#include <unistd.h>
#include <getopt.h>
@@ -25,6 +27,7 @@
#include "http.h"
#include "tcp.h"
#include "usb.h"
+#include "bonjour.h"
struct service_thread_param {
struct tcp_conn_t *tcp;
@@ -202,6 +205,9 @@ static void start_daemon()
{
// Capture USB device if not in no-printer mode
struct usb_sock_t *usb_sock;
+ // Bonjour broadcasting of the printer via Avahi
+ bonjour_t *bonjour_data = NULL;
+
if (g_options.noprinter_mode == 0) {
usb_sock = usb_open();
if (usb_sock == NULL)
@@ -213,8 +219,8 @@ static void start_daemon()
uint16_t desired_port = g_options.desired_port;
struct tcp_sock_t *tcp_socket = NULL, *tcp6_socket = NULL;
for (;;) {
- tcp_socket = tcp_open(desired_port);
- tcp6_socket = tcp6_open(desired_port);
+ tcp_socket = tcp_open(desired_port, g_options.interface);
+ tcp6_socket = tcp6_open(desired_port, g_options.interface);
if (tcp_socket || tcp6_socket || g_options.only_desired_port)
break;
// Search for a free port
@@ -259,6 +265,19 @@ static void start_daemon()
if (usb_can_callback(usb_sock))
usb_register_callback(usb_sock);
+ // Bonjour-broadcast the printer on the local machine so
+ // that cups-browsed and ippfind will discover it (does not work
+ // with the loopback interface "lo")
+ if (usb_sock && g_options.nobroadcast == 0 &&
+ strcasecmp(g_options.interface, "lo") != 0) {
+ bonjour_data = calloc(1, sizeof(bonjour_t));
+ if (bonjour_data == NULL)
+ ERR_AND_EXIT("Unable to allocate memory for Bonjour broadcast data.");
+ dnssd_init(bonjour_data);
+ register_printer(bonjour_data, usb_sock->device_id,
+ g_options.interface, real_port);
+ }
+
int i;
for (i = 0; ; i++) {
struct service_thread_param *args = calloc(1, sizeof(*args));
@@ -297,6 +316,9 @@ static void start_daemon()
}
cleanup_tcp:
+ if (bonjour_data != NULL)
+ dnssd_shutdown(bonjour_data);
+
if (tcp_socket!= NULL)
tcp_close(tcp_socket);
if (tcp6_socket!= NULL)
@@ -320,8 +342,9 @@ int main(int argc, char *argv[])
int c;
g_options.log_destination = LOGGING_STDERR;
g_options.only_desired_port = 1;
+ g_options.interface = "lo";
- while ((c = getopt(argc, argv, "qnhdp:P:s:lv:m:N")) != -1) {
+ while ((c = getopt(argc, argv, "qnhdp:P:i:s:lv:m:NB")) != -1) {
switch (c) {
case '?':
case 'h':
@@ -349,6 +372,10 @@ int main(int argc, char *argv[])
g_options.only_desired_port = 0;
break;
}
+ case 'i':
+ // Request a specific network interface
+ g_options.interface = strdup(optarg);
+ break;
case 'l':
g_options.log_destination = LOGGING_SYSLOG;
break;
@@ -374,6 +401,9 @@ int main(int argc, char *argv[])
case 'N':
g_options.noprinter_mode = 1;
break;
+ case 'B':
+ g_options.nobroadcast = 1;
+ break;
}
}
@@ -388,10 +418,15 @@ int main(int argc, char *argv[])
" -p <portnum> Port number to bind against, error out if port already taken\n"
" -P <portnum> Port number to bind against, use another port if port already\n"
" taken\n"
+ " -i <interface> Network interface to use. Default is the loopback interface\n"
+ " (lo, localhost). As the loopback interface does not allow\n"
+ " Bonjour broadcasting with Avahi, set up the dummy interface\n"
+ " (dummy0) for Bonjour broadcasting.\n"
" -l Redirect logging to syslog\n"
" -q Enable verbose tracing\n"
" -d Debug mode for verbose output and no fork\n"
" -n No-fork mode\n"
+ " -B No-broadcast mode, do not Bonjour-/DNS-SD-broadcast\n"
" -N No-printer mode, debug/developer mode which makes ippusbxd\n"
" run without IPP-over-USB printer\n"
, argv[0]);
diff --git a/src/options.h b/src/options.h
index da7a86b..3b1d7b0 100644
--- a/src/options.h
+++ b/src/options.h
@@ -24,6 +24,7 @@ struct options {
// Runtime configuration
uint16_t desired_port;
int only_desired_port;
+ char *interface;
enum log_target log_destination;
// Behavior
@@ -31,6 +32,7 @@ struct options {
int verbose_mode;
int nofork_mode;
int noprinter_mode;
+ int nobroadcast;
// Printer indentity
unsigned char *serial_num;
diff --git a/src/tcp.c b/src/tcp.c
index 46eb7ab..e621d34 100644
--- a/src/tcp.c
+++ b/src/tcp.c
@@ -20,6 +20,10 @@
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
+#include <ifaddrs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
@@ -30,7 +34,7 @@
#include "tcp.h"
-struct tcp_sock_t *tcp_open(uint16_t port)
+struct tcp_sock_t *tcp_open(uint16_t port, char* interface)
{
struct tcp_sock_t *this = calloc(1, sizeof *this);
if (this == NULL) {
@@ -46,12 +50,29 @@ struct tcp_sock_t *tcp_open(uint16_t port)
goto error;
}
+ // Find the IP address for the selected interface
+ struct ifaddrs *ifaddr, *ifa;
+ getifaddrs(&ifaddr);
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL)
+ continue;
+ if ((strcmp(ifa->ifa_name, interface) == 0) &&
+ (ifa->ifa_addr->sa_family == AF_INET))
+ break;
+ }
+ if (ifa == NULL)
+ ERR_AND_EXIT("Interface %s does not exist or IP not found.",
+ interface);
+
// Configure socket params
- struct sockaddr_in addr;
+ struct sockaddr_in addr, *if_addr;
+ if_addr = (struct sockaddr_in *) ifa->ifa_addr;
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(0x7F000001);
+ addr.sin_addr.s_addr = if_addr->sin_addr.s_addr;
+ //addr.sin_addr.s_addr = htonl(0xC0A8000F);
+ NOTE("IPv4: Binding to %s:%d", inet_ntoa(if_addr->sin_addr), port);
// Bind to localhost
if (bind(this->sd,
@@ -81,7 +102,7 @@ error:
return NULL;
}
-struct tcp_sock_t *tcp6_open(uint16_t port)
+struct tcp_sock_t *tcp6_open(uint16_t port, char* interface)
{
struct tcp_sock_t *this = calloc(1, sizeof *this);
if (this == NULL) {
@@ -97,12 +118,27 @@ struct tcp_sock_t *tcp6_open(uint16_t port)
goto error;
}
+ // Find the IP address for the selected interface
+ struct ifaddrs *ifaddr, *ifa;
+ getifaddrs(&ifaddr);
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL)
+ continue;
+ if ((strcmp(ifa->ifa_name, interface) == 0) &&
+ (ifa->ifa_addr->sa_family == AF_INET6))
+ break;
+ }
+ if (ifa == NULL)
+ ERR_AND_EXIT("Interface %s does not exist or IP not found.",
+ interface);
+
// Configure socket params
- struct sockaddr_in6 addr;
+ struct sockaddr_in6 addr, *if_addr;
+ if_addr = (struct sockaddr_in6 *) ifa->ifa_addr;
memset(&addr, 0, sizeof addr);
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
- addr.sin6_addr = in6addr_loopback;
+ addr.sin6_addr = if_addr->sin6_addr;
// Bind to localhost
if (bind(this->sd,
diff --git a/src/tcp.h b/src/tcp.h
index eda7f24..c671824 100644
--- a/src/tcp.h
+++ b/src/tcp.h
@@ -39,8 +39,8 @@ struct tcp_conn_t {
int is_closed;
};
-struct tcp_sock_t *tcp_open(uint16_t);
-struct tcp_sock_t *tcp6_open(uint16_t);
+struct tcp_sock_t *tcp_open(uint16_t, char* interface);
+struct tcp_sock_t *tcp6_open(uint16_t, char* interface);
void tcp_close(struct tcp_sock_t *);
uint16_t tcp_port_number_get(struct tcp_sock_t *);
diff --git a/src/usb.c b/src/usb.c
index 0575e33..4de7e40 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -119,11 +119,59 @@ static int is_our_device(libusb_device *dev,
}
}
+int get_device_id(struct libusb_device_handle *handle,
+ int conf,
+ int iface,
+ int altset,
+ char *buffer,
+ size_t bufsize)
+{
+ size_t length;
+
+ if (libusb_control_transfer(handle,
+ LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_ENDPOINT_IN |
+ LIBUSB_RECIPIENT_INTERFACE,
+ 0, conf, (iface << 8) | altset,
+ (unsigned char *)buffer, bufsize, 5000) < 0) {
+ *buffer = '\0';
+ return (-1);
+ }
+
+ // Extract the length of the device ID string from the first two
+ // bytes. The 1284 spec says the length is stored MSB first...
+ length = (int)((((unsigned)buffer[0] & 255) << 8) |
+ ((unsigned)buffer[1] & 255));
+
+ // Check to see if the length is larger than our buffer or less than 14 bytes
+ // (the minimum valid device ID is "MFG:x;MDL:y;" with 2 bytes for the
+ // length).
+ // If the length is out-of-range, assume that the vendor incorrectly
+ // implemented the 1284 spec and re-read the length as LSB first,..
+ if (length > bufsize || length < 14)
+ length = (int)((((unsigned)buffer[1] & 255) << 8) |
+ ((unsigned)buffer[0] & 255));
+
+ if (length > bufsize)
+ length = bufsize;
+
+ if (length < 14) {
+ // Invalid device ID, clear it!
+ *buffer = '\0';
+ return (-1);
+ }
+
+ length -= 2;
+ memmove(buffer, buffer + 2, (size_t)length);
+ buffer[length] = '\0';
+ return (0);
+}
+
struct usb_sock_t *usb_open()
{
int status_lock;
struct usb_sock_t *usb = calloc(1, sizeof *usb);
int status = 1;
+ usb->device_id = NULL;
status = libusb_init(&usb->context);
if (status < 0) {
ERR("libusb init failed with error: %s",
@@ -246,6 +294,26 @@ found_device:
const struct libusb_interface_descriptor *alt = NULL;
alt = &interf->altsetting[alt_num];
+ // Get the IEE-1284 device ID
+ if (usb->device_id == NULL) {
+ usb->device_id = calloc(2048, sizeof(char));
+ if (usb->device_id == NULL) {
+ ERR("Failed to allocate memory for the device ID");
+ goto error;
+ }
+ if (get_device_id(usb->printer, selected_config,
+ interf_num, alt_num,
+ usb->device_id, 2048) != 0 ||
+ strlen(usb->device_id) == 0) {
+ NOTE("Could not retrieve device ID for config #%d, interface #%d, alt setting #%d, will try with other combo ...",
+ selected_config, interf_num, alt_num);
+ free(usb->device_id);
+ usb->device_id = NULL;
+ } else {
+ NOTE("USB device ID: %s", usb->device_id);
+ }
+ }
+
// Skip non-IPP-USB interfaces
if (!is_ippusb_interface(alt))
continue;
diff --git a/src/usb.h b/src/usb.h
index e376a77..a46c974 100644
--- a/src/usb.h
+++ b/src/usb.h
@@ -34,6 +34,7 @@ struct usb_interface {
struct usb_sock_t {
libusb_context *context;
libusb_device_handle *printer;
+ char *device_id;
int max_packet_size;
uint32_t num_interfaces;