Working With NetBIOS in node.js
Lately I’ve been caught in the middle of a dispute between my Xerox scanner and Mac OS X. The Mac only wants to use modern versions of SMB to share files and is giving the scanner the cold shoulder. In an attempt to mediate this issue I’ve turned to hacking on ancient protocols in node.js.
So far I’ve tackled NetBIOS and thought I would share some of the code I’ve come up with.
The first step to finding or advertising a NetBIOS name is to start up a netbios-name-service instance.
1 2 3 4 5 6
Then, if you want to search for a service, like say a printer, you can execute the following code.
1 2 3 4 5 6
Note, you must use the netbios-name module in order to properly define names in order to search, advertise, or perform other operations. This module provides a simple class that combines the simple NetBIOS name with additional information such as the scope ID (aka domain name) and a suffix byte indicating the what type of node is being represented.
For the problem at hand, however, I don’t need to search for a name. Instead I want to advertise the node.js server via NetBIOS so that the scanner can push files to us.
This causes the service to monitor UDP port 137 for broadcast queries looking
for the name
XYKON-2. If a query occurs, then the service will automatically
respond with the IP address of the server.
So this allows the scanner to find our server, but what about handling the NetBIOS traffic containing the actual file operations?
To deal with this part of the problem we need to use the netbios-session module. Here is code to receive incoming NetBIOS sessions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
The netbios-session class is essentially a wrapper around a TCP socket.
new to create a new instance, you need to call
or, in this case,
attach(). Here we are using
attach() to associate the
session with a new TCP socket.
Once the session is ready to send and receive data it will emit the
event. At this point
'message' events will occur whenever a message is
received from the remote peer. Messages can be sent using the
So, now that we are receiving data from the scanner, we need to forward this on to my Mac’s SMB service listening on port 445.
Now, it turns out, direct SMB connections to port 445 actually use the NetBIOS session header to implement message framing, but it skips all of the initial session negotiation that normally occurs.
So, to achieve our forwarding we want to create a second session to port
445 using the
'direct' constructor option.
After this, we can take messages we receive from
sessionIn events and pass
them straight to
'sessionOut.write()'. Since SMB is bidirectional, we also
need to pass messages in the reverse direction. With a bit of back-pressure
logic sprinkled in, this code looks like the following:
1 2 3 4 5 6 7 8 9 10
Putting all the pieces together we end up with the following.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
We now have a fully functional proxy server for connecting devices that only speak NetBIOS up to modern, direct SMB servers.
Of course, this assumes that the underlying SMB protocol is compatible
with the destination. It turns out, the new Mac OS X SMB server has some
additional restrictions. While I can connect through the proxy using
net use on Windows XP, my scanner still fails to connect.
Looking in the log I found this:
Unfortunately this implementation is not even a temporary work around for my problem since the scanner doesn’t know how to talk Unicode.
So, it looks like I will be diving into the SMB protocol next so that I can intercept the file operations directly in node.
All the modules used in this post are available on GitHub and npm:
The end script is available on Github in the FileShift project.