This project is an unofficial TFTP server for provisioning Grandstream VoIP phones. The project has been used in the free VoIP platform Gratissip, hence the name.
It consists of three parts:
- The TFTP provisioning server.
- An interface for integrating with existing infrastructure.
- A sample interface implementation using most JDBC databases.
Basically it is a TFTP server, however with a few extensions for the Grandstream VoIP-phones.
- Normal TFTP files serving from a tftproot, this means you can use it as any other normal TFTP server.
- Serving Grandstream firmwares depending on user-choices, this means you can provision Grandstream phones with a specific firmware version automatically.
- Serving Grandstream provisional configurations, this mean you can provision Grandstream phones with settings automatically.
- Serving TFTP files hosted on a web-server (act as tftp-to-http proxy), this means you can serve files generated by a webserver over the TFTP protocol.
Grandstream is a company which sells inexpencive VoIP-phones. This is a seperate project which they may or may not be aware of. They are selling their own provosional server as a seperate product. If you want to use a provisional server professionally for your business, you should consider to buy it from them. Check their website {{http://www.grandstream.com/}}.
It is based on the fwtftpd work of Martin Kihlgren, which is GPL - and hence this is also GPL. Read more about what that means in the GplLicense. You can find Martin's work here: {{http://troja.ath.cx/~zond/fwtftpd/}}.
The project can be built using Gradle, which is already included. After downloading, run the following command:
./gradlew distZip
This produces an installation file in the folder tftpd-server/build/distribution/tftpd-server.zip
. You can unpack this file in a location of your choice. After this, you have to configure the server.
In the configuration file you can define which IP and port the TFTPD server should listen on. Normally you should leave the port to 69, the standard tftp service port, however, in special cases you can here configure it to something else. Also you can define which IP to bind on. The default 0.0.0.0 IP means to bind on all IPs available on the machine.
<bean id="tftpdServer" class="net.tanesha.tftpd.core.Server">
<property name="port" value="69" />
<property name="bindhost" value="0.0.0.0" />
<property name="vfs" ref="vfs" />
</bean>
There are four (4) kinds of different virtual filesystems supported in the server:
- Tftproot server - serves files from a folder on the filesystem.
- HTTP proxy server - serves files from a remote HTTP server.
- Grandstream firmware server - serves firmwares to Grandstream VoIP phones.
- Grandstream provisioning server - serves provisioning files to Grandstream VoIP phones.
In the configuration file you can find the bean with id "vfs". This bean holds a list of the enabled filesystems in the Tftpd server. You can add/remove from the list as you like. An example shows:
<bean id="vfs" class="net.tanesha.tftpd.core.Vfs">
<property name="filesystems">
<list>
<ref local="firmwareServer"/>
<ref local="provisionServer"/>
<!--
http proxy server is disabled
<ref local="httpProxyServer" />
-->
<ref local="tftprootServer"/>
</list>
</property>
</bean>
The order of which you configure the filesystems is important. Each virtual filesystem serves different kinds of files. The first filesystem to return a file will be the file served to the user. For example, if you configure tftprootServer as the first filesystem in the list, and a file cfg00123456789 exists, then this will be served instead of the file provided by the provisionServer later.
This filesystem serves files from a folder specified in the rootpath property. It matches all files and looks in the specified directory for files. If a file is found, it is served.
<bean id="tftprootServer" class="net.tanesha.tftpd.vfs.TftpRootServer">
<property name="rootpath" value="/tftpboot" />
</bean>
This serves files from a remote HTTP server. It is using the pattern to describe which files should be served. Some macros are available for putting into the target URL:
- %f - filename requested.
- %m - MAC address (extracted only from Grandstream phones).
<bean id="httpProxyServer" class="net.tanesha.tftpd.vfs.HTTPProxy">
<property name="pattern" value="^/?cfg[0-9a-f]{12}$"/>
<property name="target" value="http://voip/cgi-bin/tftp_request.cgi?mac=%m"/>
</bean>
Firmware needs the external interface to find information about the client and what firmware should be served.
<bean id="firmwareServer" class="net.tanesha.tftpd.vfs.FirmwareServer">
<property name="tftpdExternalInterface" ref="tftpdExternalInterface" />
<property name="versioningHelper" ref="versioningHelper" />
</bean>
Provisioning need the external interface to find information about the client and what provisioning settings should be served.
<bean id="provisionServer" class="net.tanesha.tftpd.vfs.ProvisionServer">
<property name="tftpdExternalInterface" ref="tftpdExternalInterface" />
</bean>
Should be configured in config.xml as follows:
<bean id="tftpdExternalInterface" class="net.tanesha.tftpd.jdbc.JdbcExternalImpl">
<property name="datasource" ref="myJdbcDatasource" />
<property name="queryByVersion">
<value>
select phone_id, version_no, firmware_path from firmware_versions where phone_id = ? and version_no = ?
</value>
</property>
<property name="queryLatestVersion">
<value>
select phone_id, version_no, firmware_path from firmware_versions where phone_id = ? and is_latest = 'T'
</value>
</property>
<property name="queryPhoneByMac">
<value>
select phone_id, version_no from phone_firmwares where mac_address = ?
</value>
</property>
<property name="querySettingsByMac">
<value>
select provision_attribute, provision_value from phone_settings where mac_address = ?
</value>
</property>
</bean>
<!-- please refer to spring documentation www.springframework.org for how to configure a datasource for your database -->
<bean id="myJdbcDatasource" class="...">
</bean>
After that you should be able to start your tftpd-server and see that it connects to your database and is using settings from it.
The queries above are matching a database layout as follows:
create table firmware_versions (
phone_id varchar(50),
version_no varchar(50),
firmware_path varchar(100),
is_latest varchar(1),
primary key (phone_id, version_no)
);
create table phone_firmwares (
mac_address varchar(15) primary key,
phone_id varchar(50),
version_no varchar(50)
);
create table phone_settings (
mac_address varchar(15),
provision_attribute varchar(10),
provision_value varchar(100),
primary key (mac_address, provision_attribute)
);
But, you can modify the SQL to match any existing schema you may have.
The following people have contributed, a BIG THANKS from here:
-
Steven Carey (Personal)
-
Johathan Owens (Personal)