quickfixfix-protocolquickfixn

((QuickFix.FIXT11.Logon)message).Password threw an exception like QuickFix.FieldNotFoundException


I need to implement an acceptor using QuickFix/n. The way to create and run an acceptor is as follows:

public FixAcceptorManager(FixOptions options,ILogFactory logFactory,IApplication acceptor,SessionSettings sessionSettings)
{
    _options = options;
    _acceptor = acceptor;
    _logFactory = logFactory;
    _settings = sessionSettings;
    _soketAcceptor = CreateSocketAcceptor();
    _server = new HttpServer(_options.Url, _settings);
    _isRunning = false;
}

private ThreadedSocketAcceptor CreateAcceptor()
{
    var storeFactory = new FileStoreFactory(_settings);
    var threadSocketAcceptor = new ThreadedSocketAcceptor(_acceptor, storeFactory, _settings, _logFactory);

    return threadSocketAcceptor;
}

public void StartAcceptor()
{
    if (_soketAcceptor is not null && !_isRunning)
    {
        _soketAcceptor.Start();
        _server.Start();
        _isRunning = true;
    }
}

I create sessions dynamically as follows:

public void AddSession(string targetCompId, string password)
{
    var dictionarySettings = new Dictionary();

    dictionarySettings.SetString("Password", password);

    var sessionId = new SessionID("FIXT.1.1", "TEST", targetCompId);

    _soketAcceptor.AddSession(sessionId, dictionarySettings);
}

Everything starts, sessions are added. Active sessions are displayed on the panel. I also created a simple initiator for the test:

SessionSettings settings = new SessionSettings(file);

MyClientApp application = new MyClientApp();
IMessageStoreFactory storeFactory = new FileStoreFactory(settings);
ILogFactory logFactory = new ScreenLogFactory(settings);
SocketInitiator initiator = new SocketInitiator(application, storeFactory, settings, logFactory);

application.MyInitiator = initiator;

initiator.Start();
Console.ReadLine();
initiator.Stop();

The initiator connects to the acceptor, Logon is successful.

The acceptor has the following configuration:

[DEFAULT]
BeginString=FIXT.1.1
DefaultApplVerID=FIX.5.0SP2
HeartBtInt=20
AppDataDictionary=FIXRTv1.xml
TransportDataDictionary=FIXT11.xml
#EncryptMethod = 6
FileLogPath=log
SocketAcceptPort=9823 
ConnectionType=acceptor
UseDataDictionary=Y
ResetOnLogon=Y
ResetOnLogout=Y
ResetOnDisconnect=Y
#ValidateUserDefinedFields = N
SenderCompID=MyAcceptor
StartTime=00:00:00
EndTime=23:59:00
FileStorePath=store
#AllowUnknownMsgFields = Y

And the initiator configuration:

[DEFAULT]
BeginString=FIXT.1.1
DefaultApplVerID=FIX.5.0SP2
ConnectionType=initiator
HeartBtInt=20
AppDataDictionary=FIXRTv1.xml
TransportDataDictionary=FIXT11.xml
UseDataDictionary=Y

ResetOnLogon=Y
ResetOnLogout=Y
ResetOnDisconnect=Y
FileStorePath=store
FileLogPath=log
ValidateUserDefinedFields = N
EncryptMethod = 6

[SESSION]
SenderCompID=MyInitiator
TargetCompID=MyAcceptor
Password=testtest
SocketConnectHost=127.0.0.1 
SocketConnectPort=9823

StartTime=00:00:00
EndTime=23:59:00

I pass a Password to the acceptor to set up a new session. This Password is different from the one defined in the initiator configuration.

Then I start the initiator. And it connects with its own password. Although the password the acceptor expects from this initiator is different. Moreover, in the FromAdmin method of the acceptor I can't see the Password field in any way. I also tried to use Crack:

public void FromAdmin(Message message, SessionID sessionID)
{
    Crack(message, sessionID);
}

public void OnMessage(QuickFix.FIXT11.Logon logon, SessionID s)
{
    try
    {
        var p = logon.GetString(554); // Password field in FIXT11.xml tag = 554
    }
    catch (Exception)
    {
    }
}

Attempting to remove Password from the message results in an exception. Also in Debug mode in VisualStudio, when hovering over a message for the Password field, I see the following message:

"((QuickFix.FIXT11.Logon)message).Password" threw an exception like "QuickFix.FieldNotFoundException"

The FIXRTv1.xml and FIXT11.xml files are the same. The Logon message section looks like this:

<message msgcat='admin' msgtype='A' name='Logon'>
  <field name='EncryptMethod' required='Y'/>
  <field name='HeartBtInt' required='Y'/>
  <field name='RawDataLength' required='N'/>
  <field name='RawData' required='N'/>
  <field name='ResetSeqNumFlag' required='N'/>
  <field name='NextExpectedMsgSeqNum' required='N'/>
  <field name='MaxMessageSize' required='N'/>
  <field name='TestMessageIndicator' required='N'/>
  <field name='Username' required='N'/>
  <field name='Password' required='N'/>
  <field name='NewPassword' required='N' />
  <field name='DefaultApplVerID' required='N'/>
  <component name='MsgTypeGrp' required='N'/>
</message>

Could you please tell me what I am doing wrong? I need to extract the Password field in the acceptor, validate and reject the initiator's Logon if the passwords do not match.


Solution

  • You have a lot going on in this question, so I'll try to break it up:

    "Password" is not a QF/n config setting

    This page shows the list of supported settings. "Password" isn't one of them. Your app is ignoring it.

    Likewise, your dictionarySettings.SetString("Password", password); line is also doing nothing.

    Let's focus on the initiator-- do you see a password in its outbound Logon message?

    And by "log", I mean the raw message transmissions log. I see you are using a ScreenLogFactory; I suggest you switch over to a FileLogFactory. After you run the initiator, look in the message log and find the 35=A outbound logon message. Does you see a password 554=xxx field?

    I suspect you aren't seeing it, because you aren't actually setting it (as described above).

    Try doing it via the ToAdmin callback instead:

    public void ToAdmin(Message msg, SessionID sessionID) {
        if (msg.Header.GetString(Tags.MsgType) == "A") {
            msg.SetField(new Password("mypassword");
        }
    }
    

    Now you should see, in your logs, that the Initiator is sending a password.

    One last bit: You should check for the password's presence before reading it

    Like this:

    if (login.IsSetField(554)) {
        var p = logon.GetString(554); 
    }
    

    Give all of that a try, and see how it goes.