Showing posts with label unix. Show all posts
Showing posts with label unix. Show all posts

Friday, November 12, 2010

Python: Enumerating IP Addresses on FreeBSD

As promised in my earlier post on enumerating local interfaces and their IP addresses on MacOS X, this time I'll cover how to do the same on FreeBSD and other operating systems that implement the getifaddrs API. Basically, this is just a python wrapper around the getifaddrs interface using ctypes.

The code is a bit longer than I typically like to include in a blog post, but here it goes:
"""
Wrapper for getifaddrs(3).
"""

import socket
import sys

from collections import namedtuple
from ctypes import *

class sockaddr_in(Structure):
_fields_ = [
('sin_len', c_uint8),
('sin_family', c_uint8),
('sin_port', c_uint16),
('sin_addr', c_uint8 * 4),
('sin_zero', c_uint8 * 8)
]

def __str__(self):
assert self.sin_len >= sizeof(sockaddr_in)
data = ''.join(map(chr, self.sin_addr))
return socket.inet_ntop(socket.AF_INET, data)

class sockaddr_in6(Structure):
_fields_ = [
('sin6_len', c_uint8),
('sin6_family', c_uint8),
('sin6_port', c_uint16),
('sin6_flowinfo', c_uint32),
('sin6_addr', c_uint8 * 16),
('sin6_scope_id', c_uint32)
]

def __str__(self):
assert self.sin6_len >= sizeof(sockaddr_in6)
data = ''.join(map(chr, self.sin6_addr))
return socket.inet_ntop(socket.AF_INET6, data)

class sockaddr_dl(Structure):
_fields_ = [
('sdl_len', c_uint8),
('sdl_family', c_uint8),
('sdl_index', c_short),
('sdl_type', c_uint8),
('sdl_nlen', c_uint8),
('sdl_alen', c_uint8),
('sdl_slen', c_uint8),
('sdl_data', c_uint8 * 12)
]

def __str__(self):
assert self.sdl_len >= sizeof(sockaddr_dl)
addrdata = self.sdl_data[self.sdl_nlen:self.sdl_nlen+self.sdl_alen]
return ':'.join('%02x' % x for x in addrdata)

class sockaddr_storage(Structure):
_fields_ = [
('sa_len', c_uint8),
('sa_family', c_uint8),
('sa_data', c_uint8 * 254)
]

class sockaddr(Union):
_anonymous_ = ('sa_storage', )
_fields_ = [
('sa_storage', sockaddr_storage),
('sa_sin', sockaddr_in),
('sa_sin6', sockaddr_in6),
('sa_sdl', sockaddr_dl),
]

def family(self):
return self.sa_storage.sa_family

def __str__(self):
family = self.family()
if family == socket.AF_INET:
return str(self.sa_sin)
elif family == socket.AF_INET6:
return str(self.sa_sin6)
elif family == 18: # AF_LINK
return str(self.sa_sdl)
else:
print family
raise NotImplementedError, "address family %d not supported" % family


class ifaddrs(Structure):
pass
ifaddrs._fields_ = [
('ifa_next', POINTER(ifaddrs)),
('ifa_name', c_char_p),
('ifa_flags', c_uint),
('ifa_addr', POINTER(sockaddr)),
('ifa_netmask', POINTER(sockaddr)),
('ifa_dstaddr', POINTER(sockaddr)),
('ifa_data', c_void_p)
]

# Define constants for the most useful interface flags (from if.h).
IFF_UP = 0x0001
IFF_BROADCAST = 0x0002
IFF_LOOPBACK = 0x0008
IFF_POINTTOPOINT = 0x0010
IFF_RUNNING = 0x0040
if sys.platform == 'darwin' or 'bsd' in sys.platform:
IFF_MULTICAST = 0x8000
elif sys.platform == 'linux':
IFF_MULTICAST = 0x1000

# Load library implementing getifaddrs and freeifaddrs.
if sys.platform == 'darwin':
libc = cdll.LoadLibrary('libc.dylib')
else:
libc = cdll.LoadLibrary('libc.so')

# Tell ctypes the argument and return types for the getifaddrs and
# freeifaddrs functions so it can do marshalling for us.
libc.getifaddrs.argtypes = [POINTER(POINTER(ifaddrs))]
libc.getifaddrs.restype = c_int
libc.freeifaddrs.argtypes = [POINTER(ifaddrs)]


def getifaddrs():
"""
Get local interface addresses.

Returns generator of tuples consisting of interface name, interface flags,
address family (e.g. socket.AF_INET, socket.AF_INET6), address, and netmask.
The tuple members can also be accessed via the names 'name', 'flags',
'family', 'address', and 'netmask', respectively.
"""
# Get address information for each interface.
addrlist = POINTER(ifaddrs)()
if libc.getifaddrs(pointer(addrlist)) < 0:
raise OSError

X = namedtuple('ifaddrs', 'name flags family address netmask')

# Iterate through the address information.
ifaddr = addrlist
while ifaddr and ifaddr.contents:
# The following is a hack to workaround a bug in FreeBSD
# (PR kern/152036) and MacOSX wherein the netmask's sockaddr may be
# truncated. Specifically, AF_INET netmasks may have their sin_addr
# member truncated to the minimum number of bytes necessary to
# represent the netmask. For example, a sockaddr_in with the netmask
# 255.255.254.0 may be truncated to 7 bytes (rather than the normal
# 16) such that the sin_addr field only contains 0xff, 0xff, 0xfe.
# All bytes beyond sa_len bytes are assumed to be zero. Here we work
# around this truncation by copying the netmask's sockaddr into a
# zero-filled buffer.
if ifaddr.contents.ifa_netmask:
netmask = sockaddr()
memmove(byref(netmask), ifaddr.contents.ifa_netmask,
ifaddr.contents.ifa_netmask.contents.sa_len)
if netmask.sa_family == socket.AF_INET and netmask.sa_len < sizeof(sockaddr_in):
netmask.sa_len = sizeof(sockaddr_in)
else:
netmask = None

try:
yield X(ifaddr.contents.ifa_name,
ifaddr.contents.ifa_flags,
ifaddr.contents.ifa_addr.contents.family(),
str(ifaddr.contents.ifa_addr.contents),
str(netmask) if netmask else None)
except NotImplementedError:
# Unsupported address family.
yield X(ifaddr.contents.ifa_name,
ifaddr.contents.ifa_flags,
None,
None,
None)
ifaddr = ifaddr.contents.ifa_next

# When we are done with the address list, ask libc to free whatever memory
# it allocated for the list.
libc.freeifaddrs(addrlist)

__all__ = ['getifaddrs'] + [n for n in dir() if n.startswith('IFF_')]
As always, this code is released under a BSD-style license.

Monday, July 27, 2009

Calculating MD5 of binaries without debug symbols

If you compile a binary with gcc with debugging information enabled (-g), the MD5 of the resulting binary will change depending on the name of the directory you compile it in. Which means that if two developers compile the same source code with the same options on the same machine, only they do it in their own home directories, the MD5 of the resulting binaries may differ.

However, as soon as you strip the binaries, their MD5s will be the same.

Which leads me to this little tool I whipped up to compare two binaries without stripping them:

#!/bin/sh
# Display the MD5 of a file, ignoring any debugging symbols in
# binaries.

# The strip(1)/objdump(1) commands for removing debugging
# symbols do not support writing to stdout so we need to
# allocate a temp file to write the stripped binary too.
tempfoo=`basename $0`
TMPFILE=`mktemp -q /tmp/${tempfoo}.XXXXXX`
if [ $? -ne 0 ]; then
echo "$0: Can't create temp file, exiting..."
exit 1
fi

while [ "$1" != "" ]; do
# The following line is a cheezy way to accurately
# reproduce the same error messages as md5(1) when a
# specified file is unreadable.
md5 "$1" > /dev/null
if [ $? -eq 0 ]; then

# Try to strip symbols from the file on the
# assumption it is a binary and, if successful,
# compute the md5 of the stripped file. Note that
# objcopy is the same as the strip(1) command. If
# objcopy failed to parse the file (i.e. because it
# is not in ELF format), simply compute the md5 of
# the whole file since there are no debugging symbols
# to strip.
m=`(objcopy -g "$1" $TMPFILE >/dev/null 2>&1 && \
md5 -q $TMPFILE;) || md5 -q "$1"`

# Output the result in a md5(1)-compatible format.
echo "MD5($1) = $m"
fi
shift
done

rm $TMPFILE

Thursday, July 2, 2009

su Forces Terminal Echo to On

I just discovered an interesting quirk of the su command, at least on FreeBSD: it forces terminal echo to on and you cannot turn it back off. Interestingly, sudo does not have the same behavior. Here is a sample terminal session showing the behavior (input that was not echoed is shown in red):

[14:54:35] myhost:~ $ stty
speed 38400 baud;
lflags: echoe echok echoke echoctl pendin
iflags: -ixany -imaxbel
oflags: -oxtabs
cflags: cs8 -parenb
[14:54:38] myhost:~ $ stty -echo
[14:54:57] myhost:~ $ stty[enter] speed 38400 baud;
lflags: -echo echoe echok echoke echoctl pendin
iflags: -ixany -imaxbel
oflags: -oxtabs
cflags: cs8 -parenb
[14:55:00] myhost:~ $ sudo -s[enter] Password: mypassword[enter]
[14:55:15] myhost:~ # stty[enter] speed 38400 baud;
lflags: -echo echoe echok echoke echoctl pendin
iflags: -ixany -imaxbel
oflags: -oxtabs
cflags: cs8 -parenb
[14:55:20] myhost:~ # exit
[14:55:25] myhost:~ $ su[enter] Password: rootpassword[enter]
myhost# stty
speed 38400 baud;
lflags: echoe echok echoke echoctl pendin
iflags: -ixany -imaxbel
oflags: -oxtabs
cflags: cs8 -parenb
myhost# stty -echo
myhost# stty
speed 38400 baud;
lflags: echoe echok echoke echoctl pendin
iflags: -ixany -imaxbel
oflags: -oxtabs
cflags: cs8 -parenb
myhost#

As you can see, the -echo flag was preserved by the sudo session, but not be the su session. Furthermore, it was not possible to re-disable terminal echo within the su session -- it simply ignored the directive.

I suspect the cause is somewhere in the PAM libraries that su uses to authenticate the user. However, I would have expected sudo to use the same PAM libraries, so I cannot explain the difference in behavior.

Wednesday, November 7, 2007

Unix storage solution

I've been lax in updating my blog lately, mainly due to lack of Internet access at my apartment since we've moved to Tokyo. It is nice and all that I have a variety of high-speed Internet service providers to choose from, but it would be really great if any of them could actually hook up my service is less than a month. :(

Anyway, until I get my own Internet service, I've been "borrowing" access via an open AP with a signal just strong enough that I can associate if I stand right next to my window. Sitting is no good; I have to have the laptop pretty high in the air to get a signal. As you can imagine, I've been reluctant to get on the Internet any more than I have to.

That said, I couldn't pass up the opportunity to share a surprising Unix storage solution I discovered today. Actually, my wife picked it up unintentionally while shopping at the Don Quixote across the street...


I kid you not, these are cheap little plastic refrigerator storage boxes. I couldn't believe my eyes when she showed them to me. Now if I could only find ones that said "FreeBSD" and "Linux" I'd be set on souvenirs the next time I go back to the U.S.