Sending Login Packet to Minecraft Server in Python Not Working

Jack picture Jack · Mar 4, 2012 · Viewed 7.8k times · Source

I have the following script in Python. What it does is tries to connect to a MineCraft server, first by sending a 'handshake', then sending a login request. The protocol specs can be found here: http://wiki.vg/Protocol

Anyway, the python script works fine, and there are no errors. However, I'm fairly use I encoded the second packet wrong, as when it is sent, nothing appears on the server console. The player isn't connected or anything. It just eventually times out and closes the connection due to the 'client' not logging in in time.

Basically, anyway who has experience with struct.pack() should be able to help me here. I have commented the line where I am unsure of whether I have encoded everything right. The detailed information on packing the data is shown in the link above.

Any help would be greatly appreciated, I'm pretty clueless with encoding/packing data. :(

import struct
import socket
import time
import urllib
import urllib2
host = "127.0.0.1"
port = 25566
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

logindata = {'user':'JackBeePee', 'password':'*******', 'version':'12'}
data = urllib.urlencode(logindata)
print('Sending data to login.minecraft.net...')
req = urllib2.Request('https://login.minecraft.net', data)
response = urllib2.urlopen(req)
returndata = response.read() 
returndata = returndata.split(":")
mcsessionid = returndata[3]
del req
del returndata
print("Session ID: " + mcsessionid)
data = {'user':u'JackBeePee','host':u'127.0.0.1','port':25566}


stringfmt = u'%(user)s;%(host)s:%(port)d'
string = stringfmt % data
structfmt = '>bh'
packetbytes = struct.pack(structfmt, 2, len(string))+string.encode('utf-16BE')
s.send(packetbytes)
connhash = s.recv(1024)
print("Connection Hash: " + connhash)
print('Sending data to http://session.minecraft.net/game/joinserver.jsp?user=JackBeePee&sessionId=' + mcsessionid + '&serverId=' + connhash + '...')
req = urllib.urlopen('http://session.minecraft.net/game/joinserver.jsp?user=JackBeePee&sessionId=' + mcsessionid + '&serverId=' + connhash)
returndata = req.read()
if(returndata == 'OK'):
    print('session.minecraft.net says everything is okay, proceeding to send data to server.')
else:
    print('Oops, something went wrong.')

time.sleep(5)

# All above here works perfectly.
enc_user = data['user'].encode('utf-16BE')
#This line is probably where something's going wrong:
packetbytes = struct.pack('>bih', 1, 23, len(data['user'])) + data['user'].encode('utf-16BE') + struct.pack('>hiibBB', 2,0,0,0,0,0)
print(len(packetbytes))
print('Sending ' + packetbytes + ' to server.')
s.send(packetbytes)

while True:
    data = s.recv(1024)
    if data:
        print(data)

Answer

Roland Smith picture Roland Smith · Mar 4, 2012

It seems you're not adding the string or the empty fields;

packetbytes = struct.pack('>bihshiibBB', 1, 23, len(data['user']), enc_user, 0, 0, 0, 0, 0, 0)

About the second h in the packing format, a Minecraft string is greater or equal than 2 bytes in length. So I'm assuming that an empty string is just a short with the value 0.

Edit: The abovementioned method doesn't work; the s needs a length;

packfmt = '>bih{}shiibBB'.format(len(enc_user))
packetbytes = struct.pack(packfmt, 1, 23, len(data['user']), enc_user, 0, 0, 0, 0, 0, 0)