You can do this with a little custom code in extensions_custom.conf
I suggest adding two contexts: The first is identical to from-pstn-toheader except that it transfers control to your Durchwahl check. The second checks for the range of numbers used for direct dial extensions. Called numbers outside this range are treated normally (routed to your IVR, queue, attendant, etc.)
[from-pstn-toheader-durchwahl]
exten => _.,1,NoOp(Attempting to extract DID from SIP To header)
exten => _.,n,gotoif($["${CHANNEL(channeltype)}"="SIP"]?SIP)
exten => _.,n,gotoif($["${CHANNEL(channeltype)}"="PJSIP"]?PJSIP)
exten => _.,n,NoOp(Unable to determine SIP channel type)
exten => _.,n,goto(from-pstn-durchwahl,${EXTEN},1))
exten => _.,n(SIP),Goto(from-pstn-durchwahl,${CUT(CUT(SIP_HEADER(To),@,1),:,2)},1)
exten => _.,n(PJSIP),Goto(from-pstn-durchwahl,${CUT(CUT(PJSIP_HEADER(read,To),@,1),:,2)},1)
[from-pstn-durchwahl]
exten => _08923456XXX,1,Goto(from-did-direct,${EXTEN:8},1) ;strip the first 8 numbers so we can match the 3-digit extensions
include => from-pstn
Replace the 08923456 with the actual digits your trunking provider sends before the extension number and change the number after EXTEN: to the number of digits before the extension number.
Then, set the context for the trunk(s) involved to from-pstn-toheader-durchwahl.
Note that I did not test this code and it may contain typos or other errors. If you have trouble, post a log of a failing incoming call.