Building a Custom Update Server for Polycom VOIP Phones
Introduction
At work, we had several Polycom phones in our remote offices stop working due to our VOIP provider discontinuing TLS v1.0 without any warning. This was made worse by the fact that our VOIP provider decided they didn’t need to host firmware files for these phones to update to a version that supports newer versions of the TLS standard… and hadn’t been doing so for years.
As a result of this, I was tasked with building out a process for applying updates to Polycom phones without utilizing the update server hosted by our VOIP provider.
Finding ANY documentation on this whatsoever was effectively impossible, so I’m making this post to help the next person who has to do this preserve their sanity.
So, without further interruption to what should be a simple guide, we’ll cover setting up an FTP server, configuring firmware files, and finally updating your phones.
Procedure
Setting up the FTP Server
Polycom phones require an FTP server to receive update files from. This doesn’t have to be anything complicated. It just needs to be able to serve files.
Python (jumpscare) is among the quickest ways to achieve this.
First, we’ll need to install pyftpdlib. A Python library that allows us to host a basic FTP server:
# Most environments:
pip install pyftpdlib
# Ubuntu 24 Server (and probably a lot of other Debian-based distros)
sudo apt install python3-pyftpdlib
Now, we need to host the server. At the time of writing, Polycom phones default to using the user/password PlcmSpIp/PlcmSpIp. If this changes, update the script below.
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
# Create authorizer
authorizer = DummyAuthorizer()
# Anonymous user with read-only permissions
authorizer.add_anonymous(".", perm="lr")
# Add user (PlcmSpIp) with read-only permissions
authorizer.add_user("PlcmSpIp", "PlcmSpIp", ".", perm="lr")
# Create handler
handler = FTPHandler
handler.authorizer = authorizer
# Create and start server
server = FTPServer(("0.0.0.0", 21), handler)
server.serve_forever()
Various notes on directory structure
The remainder of this document assumes that the root/working directory for the FTP server contains the following:
├── FTP
│ ├── Polycom-UC-Software-4.0.15-rts22-release-sig-combined
│ ├── Polycom-UC-Software-4.0.15-rts22-release-sig-split
│ ├── Polycom-UC-Software-6.3.1-rts45-M-release-sig-combined
│ ├── Polycom-UC-Software-6.3.1-rts45-M-release-sig-split
│ ├── Poly_UC_Software_5_9_8_release_sig_split
│ ├── Poly_UC_Software_5.9.8_rts63_release_sig_combined
│ ├── README.md
│ ├── the_liquid_chocolate_experience.xml
│ ├── UC_Software_6_3_1_AE_release_sig_split
│ ├── UC_Software_6_4_3_I_release_sig_split
│ ├── UC_Software_6_4_5_release_sig_split
│ ├── UC_Software_6_4_6_release_sig_split
│ └── ZIP_files
The various Polycom
and UC_Software
directories contain extracted versions of the firmware files.
Visit the Polycom Firmware Matrix to locate the software packages for your specific device.
The ZIP_files
directory contains unextracted and .zip files downloaded from Polycom, largely just for reference.
A quick and easy way to bulk-extract ZIP files under a Linux shell:
for file in *.zip; do
dirname="${file%.zip}"
mkdir -p "$dirname"
unzip "$file" -d "$dirname"
done
Ultimately, the phone doesn’t care about the directory structure. You can organize the directory structure however you want as the phone pulls update files down according to the XML file.
Building out the XML file
We’ll need to create an XML configuration file that tells the phone where to download the firmware image from. In my examples, I will be using ftp://2008.obama.cc/
as this is what I used when testing this process for the first time.
The file the_liquid_chocolate_experience.xml
is what the phone gets pointed at to check for updates. In our case, the URL would be ftp://2008.obama.cc/the_liquid_chocolate_experience.xml
.
Predictably, the name of the name of the XML file doesn’t matter. I just like messing up my digital footprint :)
So, what is the magic sauce inside the XML file?
<PHONE_IMAGES>
<REVISION ID="3111-48350-001">
<PHONE_IMAGE>
<VERSION>5.9.8.5760</VERSION>
<PATH>ftp://2008.obama.cc/Poly_UC_Software_5_9_8_release_sig_split/3111-48350-001.sip.ld</PATH>
</PHONE_IMAGE>
</REVISION>
</PHONE_IMAGES>
This looks extremely haunted, and I admit that it is, but it’s actually very simple, so let’s start from the beginning:
-
REVISION ID={}
is the hardware revision that corresponds to the phone’s part number. This can be quickly and easily obtained through the phone’s web portal home page. In the case of the Polycom VVX 311 I’m working with while writing this, it is3111-48350-001 Rev:A
. So, to fill out the XML file, we just need to removeRev:A
. -
PHONE_IMAGE
represents an individual software version for the phone. If there are multiple software versions for a given phone, you just create another<PHONE_IMAGE>
category underneath the revision for a given phone. I’ve demonstrated what this looks like a little further down. -
VERSION
is pretty arbitrary. You can literally set it to any string as far as I can tell (there doesn’t appear to be any sort of length limit either). If we want to represent the exact version number for a given update file, there is a file included in each firmware package calledsip.ver
. Open it in a text editor to view the exact version information. In this case, I am working with software version5.9.8.5760
. -
PATH
represents the location on an FTP server that the phone can download the firmware image from. Open your extracted firmware directory and find the the relevant file for your phone’s part number. In my case, for the Polycom VVX 311, I am looking for a file named3111-48350-001.sip.ld
.- Using the directory structure I described previously the path to the update file is as follows:
ftp://2008.obama.cc/Poly_UC_Software_5_9_8_release_sig_split/3111-48350-001.sip.ld
.
- If you’re confused about where the
3111-48350-001
came from, see the details for theREVISION
field.
- Using the directory structure I described previously the path to the update file is as follows:
So, putting it all together (along with some settings for the Polycom VVX 600), we get a config file that looks something like this:
<PHONE_IMAGES>
<!-- Polycom VVX 311 (and 301??) -->
<REVISION ID="3111-48350-001">
<PHONE_IMAGE>
<VERSION>5.9.8.5760</VERSION>
<PATH>ftp://2008.obama.cc/Poly_UC_Software_5_9_8_release_sig_split/3111-48350-001.sip.ld</PATH>
</PHONE_IMAGE>
</REVISION>
<!-- Polycom VVX 600 -->
<REVISION ID="3111-44600-001">
<PHONE_IMAGE>
<VERSION>6.4.6.2453</VERSION>
<PATH>ftp://2008.obama.cc/UC_Software_6_4_6_release_sig_split/3111-44600-001.sip.ld</PATH>
</PHONE_IMAGE>
<PHONE_IMAGE>
<VERSION>5.9.8.5760</VERSION>
<PATH>ftp://2008.obama.cc/Poly_UC_Software_5_9_8_release_sig_split/3111-44600-001.sip.ld</PATH>
</PHONE_IMAGE>
</REVISION>
</PHONE_IMAGES>
Putting it all together
Ensure port 21 is open on your server’s firewall so that devices can reach the FTP server.
Now, simply enter the directory where you have the .XML file and execute the Python FTP server. I placed it in the parent directory of FTP server as to avoid serving the Python script:
python ../ftpserver.py
And finally, head to the phone’s web console and enter the software upgrade screen (Utilities -> Software Upgrade), then select custom server, and enter the FTP address to your .XML file: ftp://2008.obama.cc/the_liquid_chocolate_experience.xml
.
After this, you should see the various firmware versions you configured. Simply select one, then wait for the update to finish.