Python SMTP library can't handle IPv6 - python

How can i add IPv6 option on SMTP library please ?
When i try to connect to SMTP using IPv6 i got this error :
smtpserver = smtplib.SMTP("smtp.gmail.com", 587, source_address=('2a00:xxxx:5::1e7', 80, 0, 0))
Error : TypeError: AF_INET address must be a pair (host, port)
Which means smtplib.py doesn't support IPv6
I found an article about that "https://bugs.python.org/issue3461" . I tried to add the patch into my smtplib.py but it didn't work
Can anyone please help me to do this ?

... source_address=('2a00:xxxx:5::1e7', 80, ...
The error message is misleading. Doing an strace on Linux shows that it actually tries to bind to the given IP and port but fails - and then issues this misleading error message. Why it fails can have various reason, but typical cases might be that it is no permission to bid to the port (binding to port 80 as done here needs root privileges) or that the port is already in use by another socket (port 80 is usually used for web servers).
In general it is better to only specify the IP address and leave the port to 0. This way it will use the given IP address as source IP but pick a random (ephemeral) port as source port.
I found an article about that "https://bugs.python.org/issue3461"
This is unrelated. It is only about the sender shown in the EHLO command during the SMTP dialog. If this is a problem just give the value to use with the local_hostname argument.

Python 3's socket.create_connection obtains the first DNS resolution entry of the target address that is connectable (and this is a behaviour that is documented in Python as unstable by the DNS design).
https://github.com/python/cpython/blob/fdcb675e/Lib/socket.py#L824
The CPython implementation _socket.connect throws the error in question on seeing a mismatch between the socket's address family at create time and the address family at another time,
https://github.com/python/cpython/blob/1aa6be0/Modules/socketmodule.c#L1807
Looking closer at socket.create_connection, I guess the source_address parameter cannot control the address family to use for the target. The socket object sock is created in create_connection with the address family of the resolved target address. Then source_address is used to specify the object's source with a call to sock.bind(source_address). It appears that at this point, if the resolved target was IPv4 but source_address was IPv6, the error is thrown?
As a possible fix that would not monkey-patch socket.create_connection, I can imagine that instead of providing a symbolic host name in the target address, the caller resolves the host name first to an A or an AAAA entry, depending on the desired family. The caller supplies the numeric recod as the target address. The source address's family should be in accord with the desired family, too.
In other words, the caller can use socket.getaddrinfo to find the required target address by the desired family. Other callers such as users of the request library can hot-wire socket.create_connection with the same idea.
https://docs.python.org/3/library/socket.html#socket.getaddrinfo

Related

How to use Python SMB connection when you don't know the server's IP?

I'm using PySMB right now:
https://pysmb.readthedocs.io/en/latest/api/smb_SMBConnection.html
and the SMBConnection.connect spec is problematic because it requires knowing the server's IP address.
What about a usage case where I don't know the IP address and looking up the IP address fails? I already went through the steps in https://apple.stackexchange.com/questions/10956/finding-the-remote-ip-address-used-by-a-mounted-smb-share although the server in question isn't a Bonjour service.
I tested using the smb address (that would normally be typed into the Finder's "Connect To Server" option in Mac OS) in the connect function and that didn't work.
Is there an alternative library that takes an SMB address (instead of IP address), or at least a canonical/proper way to translate that into an IP address for this? Either way, please post an example.
I was facing similar kind of issue, did u try making the following changes?
smb = SMBConnection(user_id, password, client, server_name, domain = domain, use_ntlm_v2=True, is_direct_tcp=True)
ip = socket.gethostbyname(server_name)
print(ip)
smb.connect(server_name, 445)

Find VirusTotal API IP address and port number? [duplicate]

We can find out IP address of a domain name or URL. But how to find out Port number on which a domain name is hosted?
Unfortunately the standard DNS A-record (domain name to IP address)
used by web-browsers to locate web-servers does not include a port
number. Web-browsers use the URL protocol prefix (http://) to
determine the port number (http = 80, https = 443, ftp = 21, etc.)
unless the port number is specifically typed in the URL (for example
"http://www.simpledns.com:5000" = port 5000).
Can I specify a TCP/IP port number for my web-server in DNS? (Other than the standard port 80)
Quite an old question, but might be helpful to somebody in need.
If you know the url,
open the chrome browser,
open developer tools in chrome ,
Put the url in search bar and hit enter
look in network tab, you will see the ip and port both
DNS server usually have a standard of ports used. But if it's different, you could try nmap and do a port scan like so:
> nmap 127.0.0.1
The port is usually fixed, for DNS it's 53.
If it is a normal
then the port number is always 80
and may be written as http://www.somewhere.com:80
Though you don't need to specify it as :80 is the default of every web browser.
If the site chose to use something else then they are intending to hide from anything not sent by a "friendly" or linked to.
Those ones usually show with https
and their port number is unknown and decided by their admin.
If you choose to runn a port scanner trying every number nn from say 10000 to 30000 in https://something.somewhere.com:nn
Then your isp or their antivirus will probably notice and disconnect you.
Use of the netstat -a command will give you a list of connections to your system/server where you are executing the command.
For example it will display as below, where 35070 is the port number
TCP 10.144.0.159:**52121** sd-s-fgh:35070 ESTABLISHED
Port numbers are defined by convention. HTTP servers generally listen on port 80, ssh servers listen on 22. But there are no requirements that they do.
domain = self.env['ir.config_parameter'].get_param('web.base.url')
I got the hostname and port number using this.

IPv6 Address to supply for Python socket source

I've set up a VM and am trying to use a Python script to send IPv6 messages to my computer so I can analyze it using WireShark. The host computer is Windows, and I want to see messages from a Linux VM. However, when I try to send messages, socket.bind() returns with an invalid argument error. What IPv6 address should I use as the local IP for binding sockets? I'm 99% sure that the error is coming from binding to an invalid IP. So what should I use as the IP? Here is the output of nmcli dev show, hopefully this is enough information to help me figure this out. If it's not, let me know and I'll add more info.
[eng#peter test_scripts]$ nmcli dev show
GENERAL.DEVICE: enp0s3
GENERAL.TYPE: ethernet
GENERAL.HWADDR: 08:00:27:F7:9A:17
GENERAL.MTU: 1500
GENERAL.STATE: 100 (connected)
GENERAL.CONNECTION: System enp0s3
GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/0
WIRED-PROPERTIES.CARRIER: on
IP4.ADDRESS[1]: 10.0.2.15/24
IP4.GATEWAY: 10.0.2.2
IP4.DNS[1]: 10.0.2.3
IP4.DOMAIN[1]: stc.syrres.com
IP6.ADDRESS[1]: fe80::a00:27ff:fef7:9a17/64
IP6.GATEWAY:
GENERAL.DEVICE: lo
GENERAL.TYPE: loopback
GENERAL.HWADDR: 00:00:00:00:00:00
GENERAL.MTU: 65536
GENERAL.STATE: 10 (unmanaged)
GENERAL.CONNECTION: --
GENERAL.CON-PATH: --
IP4.ADDRESS[1]: 127.0.0.1/8
IP4.GATEWAY:
IP6.ADDRESS[1]: ::1/128
IP6.GATEWAY:
I've tested 'fe80::a00:27ff:fef7:9a17/64', 'fe80::a00:27ff:fef7:9a17' and others, but still can't get it to bind. What IPv6 address should I use?
If you want to listen, your best bet is to bind to :: which is the equivalent of binding to 0.0.0.0.
If you want to connect to that server, keep in mind you are using link-local addresses, which require a scope ID in order to function properly.
For example, on Linux, to connect to host fe80::1 on interface eth0 you would connect to fe80::1%eth0. If you're dealing with the socket module, don't forget to either use getaddrinfo() or be very careful to populate scopeid.

Python dhcp client

I'm trying to learn how to directly (no libraries) send DHCP request from python on multi-homed machine (multiple interfaces).
I've looked at pydhcplib, but still do not get it.
This code send DHCP packet on specific interface (eth3 in my case - no IP assigned), but it sends with eth0 IP address. How to change my src IP to 0.0.0.0?
dhcp-message is truncated in this example
LOCAL_PORT=68
SERVER_PORT=67
LOCAL_IP="0.0.0.0"
BCAST_IP="255.255.255.255"
LISTEN_DEV="eth3"
MSG_SIZE=2048
Conn=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Conn.settimeout(5)
Conn.setsockopt(socket.SOL_SOCKET,IN.SO_BINDTODEVICE,LISTEN_DEV+'\0')
Conn.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)
Conn.bind((LOCAL_IP, LOCAL_PORT))
# Create DHCP-Discovery
msg="010106003f7d1664......"
Conn.sendto(msg.decode("hex"),(BCAST_IP,SERVER_PORT))
received = Conn.recv(MSG_SIZE)
Conn.close()
I assume you already know about the Advanced Interprocess Communication Tutorial.
Spoiler Alert: If you want to jump straight to the bottom line, have a look at the DHCP Query recipe.
Edit:
The special value INADDR_ANY (0.0.0.0, or the empty string '' in a python socket) is not an IP address.
"When an address is specified as INADDR_ANY (a manifest constant
defined in < netinet/in.h >), the system interprets the address as 'any
valid address'."
From RFC 2131:
In the case of a client using DHCP for initial configuration
(before the client's TCP/IP software has been completely
configured), DHCP requires creative use of the client's TCP/IP
software and liberal interpretation of RFC 1122. The TCP/IP
software SHOULD accept and forward to the IP layer any IP packets
delivered to the client's hardware address before the IP address is
configured; DHCP servers and BOOTP relay agents may not be able to
deliver DHCP messages to clients that cannot accept hardware
unicast datagrams before the TCP/IP software is configured.
Presumably you're running this program on a system where the ethernet interfaces have already been configured and have valid IP addresses. I'm not sure why you'd want the source IP address to be 0.0.0.0, but perhaps you could set the interface IP to 0.0.0.0 with ifconfig to get the effect you want.
Or you could use a RAW socket and build the IP and UDP headers yourself to contain anything.

Twisted: source IP address for outbound connections

I'm in the process of implementing a service -- written in Python with the Twisted framework, running on Debian GNU/Linux -- that checks the availability of SIP servers. For this I use the OPTIONS method (a SIP protocol feature), as this seems to be a commonplace practice. In order to construct correct and RFC compliant headers, I need to know the source IP address and the source port for the connection that is going to be established. [How] can this be done with Twisted?
This is what I tried:
I subclassed protocol.DatagramProtocol and within startProtocol(self) I used self.transport.getHost().host and self.transport.getHost().port. The latter is indeed the port that's going to be used, whereas the former only yields 0.0.0.0.
I guess that at this point Twisted doesn't [yet?] know which interface and as such which source IP address will be used. Does Twisted provide a facility that could help me with this or do I need to interface with the OS (routing) in a different way? Or did I just use self.transport.getHost().host incorrectly?
For the sake of completeness I answer my own question:
Make sure you use connect() on the transport before trying to determine the host's source IP address. The following excerpt shows the relevant part of a protocol implementation:
class FooBarProtocol(protocol.DatagramProtocol):
def startProtocol(self):
self.transport.getHost().host # => 0.0.0.0
self.transport.connect(self.dstHost, self.dstPort)
self.transport.getHost().host # => 192.168.1.102
If you are using UDP then the endpoint is determined by either:
calling bind() on the socket and explicitly giving it an address
sending a packet
If you want a few more details, check this response.
The problem is that I'm not that familiar with twisted. From what I can tell by a quick perusal of the source, it looks like you might want to use a reactor like t.i.d.SelectReactor instead. This is what t.n.d.DNSDatagramProtocol does under the hood.
If you take twisted out of the picture, then the following snippet shows what is going on:
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
<socket._socketobject object at 0x10025d670>
>>> s.getsockname() # this is an unbound or unnamed socket
('0.0.0.0', 0)
>>> s.bind( ('0.0.0.0', 0) ) # 0.0.0.0 is INADDR_ANY, 0 means pick a port
>>> s.getsockname() # IP is still zero, but it has picked a port
('0.0.0.0', 56814)
Get the host name is a little trickier if you need to support multiple network interfaces or IPv4 and IPv6. If you can make the interface used configurable, then pass it in as the first member of the tuple to socket.bind() and you are set.
Now the hard part is doing this within the confines of the abstractions that twisted provides. Unfortunately, I can't help a whole lot there. I would recommend looking for examples on how you can get access to the underlying socket or find a way to pass the socket information into the framework.
Good luck.
Did you see if that you want to do is possible with the SIP implementation that is part of Twisted?
In any case, how you set the source address and port for UDP in Twisted is quite similar to how you set them without Twisted. In Twisted, reactor.listenUDP(port, protocol, interface) binds an UDP socket to a specific port and interface and handles the received datagrams to your protocol. Inside the protocol, self.transport.write(msg, addr) sends a datagram to addr using the address that the protocol is bound to as source address.
Reading your question again, I think the only part you were missing was passing interface to reactor.listenUDP(...).

Categories